【问题标题】:Error with single quotes inside text in select statement选择语句中文本内的单引号错误
【发布时间】:2015-04-29 18:51:39
【问题描述】:

使用 Postgresql 9.3 得到错误:

select 'hjhjjjhjh'mnmnmnm'mn'

错误:

ERRO:“'mn'”中或旁边的语法错误
SQL 状态:42601
字符:26

我尝试将文本中的单引号替换为:

select REGEXP_REPLACE('hjhjjjhjh'mnmnmnm'mn', '\\''+', '''', 'g')

select '$$hjhjjjhjh'mnmnmnm'mn$$'

但它不起作用。

下面是真正的代码:

CREATE OR REPLACE FUNCTION generate_mallet_input2() RETURNS VOID AS $$ 
DECLARE 
sch name;
r record;   
BEGIN

    FOR sch IN 
     select schema_name from information_schema.schemata where schema_name not in ('test','summary','public','pg_toast','pg_temp_1','pg_toast_temp_1','pg_catalog','information_schema') 
    LOOP 

         FOR r IN EXECUTE 'SELECT rp.id as id,g.classified as classif, concat(rp.summary,rp.description,string_agg(c.message, ''. '')) as mess
            FROM ' || sch || '.report rp
            INNER JOIN ' || sch || '.report_comment rc ON rp.id=rc.report_id
            INNER JOIN ' || sch || '.comment c ON rc.comments_generatedid=c.generatedid 
            INNER JOIN ' || sch || '.gold_set g ON rp.id=g.key
            WHERE g.classified = any (values(''BUG''),(''IMPROVEMENT''),(''REFACTORING''))
            GROUP BY g.classified,rp.summary,rp.description,rp.id'
         LOOP

            IF r.classif = 'BUG' THEN
                EXECUTE format('Copy( select REPLACE(''%s'', '''', '''''''') as m ) To ''/tmp/csv-temp/BUG/'|| quote_ident(sch) || '-' || r.id::text || '.txt ''',r.mess);
            ELSIF r.classif = 'IMPROVEMENT' THEN
                EXECUTE format('Copy( select REPLACE(''%s'', '''', '''''''') as m ) To ''/tmp/csv-temp/IMPROVEMENT/'|| quote_ident(sch) || '-' || r.id || '.txt '' ',r.mess);
            ELSIF r.classif = 'REFACTORING' THEN
                EXECUTE format('Copy( select REPLACE(''%s'', '''', '''''''') as m ) To ''/tmp/csv-temp/REFACTORING/'|| quote_ident(sch) || '-' || r.id || '.txt '' ',r.mess);
            END IF;             

         END LOOP;



    END LOOP; 
    RETURN; 

END;
$$ LANGUAGE plpgsql STRICT; 

select * FROM generate_mallet_input2();

错误:

错误:erro de sintaxe em ou próximo a "mailto" LINHA 1: ...e.http.impl.conn.SingleClientConnManager$HTTPCLIENT-803)。
新的 SSLSocketFactory.connectSocket 方法使用 InetSocketAddress.getHostName() 参数调用 X509HostnameVerifier。当所选的IP地址具有反向查找名称时,将使用已解析的名称调用验证者,因此IP检查失败。
4.0版本检查原始IP /主机名,但无法使用新的ConnectSocket()完成。方法。
TestHostnameVerifier.java 仅检查 127.0.0.1/.2 并因此掩盖了问题,因为匹配的证书同时具有“localhost”和“127.0.0.1”,但实际上只有“localhost”匹配。使用 8.8.8.8 的测试用例会更好。我为不需要反向 DNS 查找的问题提交了一个稍微更好的解决方法。

Oleg。我不得不求助于一个相当丑陋的黑客来解决这个问题。更好的解决方案需要更改 X509HostnameVerifier API。我觉得 X509HostnameVerifier 接口的弃用是没有保证的,因为在证书中使用 CN 的 IP 地址本身就是一种黑客攻击。

请查看。

Oleg 。即使是第二个也需要服务器提供受信任的证书。我认为这两种情况没有太大区别。测试错误。尝试连接到https://93.62.162.60:8443/。证书的 CN=93.62.162.60,但检查的是 93-62-162-60.ip23.fastwebnet.it。嗯,我的评论并不是要恢复补丁。第一种情况已经可以利用,现在仍然可以利用。您的补丁是不破坏 API 的“正确”解决方案。

但为了避免任何安全问题(包括已经存在的问题),必须更改 API。我无法重现该问题。与远程对等方的 SSL 连接通过默认主机名验证。

---
执行 requestGET https://www.verisign.com/ HTTP/1.1
[DEBUG] SingleClientConnManager - 获取路由 HttpRoute 的连接[{ s}->https://www.verisign.com]
[DEBUG] DefaultClientConnectionOperator - 连接到www.verisign.com/69.58.181.89:443
[DEBUG] RequestAddCookies - 已选择 CookieSpec:最佳匹配
[DEBUG] DefaultHttpClient - 尝试执行 1 request
[DEBUG] DefaultClientConnection - 发送请求:GET / HTTP/1.1
[DEBUG] headers - >> GET / HTTP/1.1
[DEBUG] headers - >> Host: www.verisign.com
[DEBUG] 标头 - >> 连接:Keep-Alive
[DEBUG] 标头 - >> 用户代理:Apache-HttpClient/4.1 (java 1.5)
[DEBUG] DefaultClientConnection - 接收响应:HTTP/1.1 200 OK
[DEBUG] 标头 - [DEBUG] 标头 - [DEBUG] 标头 - [DEBUG] 标头 - [DEBUG] 标头 - [DEBUG] 标头 - [DEBUG] 标头 - [DEBUG] 标头 - [DEBUG] 标头 - [DEBUG] 标头 - [DEBUG ] ResponseProcessCookies - Cookie 接受:“[版本:0][名称:v1st][值:D732270AE4FC9F76][域:.verisign.com][路径:/][到期:2 月 19 日星期三 15:28:00 GMT+01: 00 2020]”。
[DEBUG] ResponseProcessCookies - Cookie 已接受:“[版本:0][名称:v1st][值:D732270AE4FC9F76][域:.verisign.com][路径:/][到期时间:2 月 19 日星期三 15:28: 00 GMT+01:00 2020]”。
[DEBUG] DefaultHttpClient - 连接可以保持活动 5000 MILLISECONDS
------------------------------- ---------
HTTP/1.1 200 OK
响应内容长度:-1
[DEBUG] SingleClientConnManager - 释放连接 org.apache.http.impl.conn.SingleClientConnManager$ConnAdapter@ 15ad5c6
[调试] DefaultClientConnection - 连接关闭
---

您是否有机会使用自定义 SSL 套接字工厂?它是否实现了 LayeredSchemeSocketFactory 接口?

Oleg。很好的工作,很好的补丁,谢谢!嗯,我看了一下补丁。它应该可以解决问题(尽管不完全,因为反向查找可能会给出错误/无法解析的主机名),但正如您所说,这是一个粗略的黑客攻击,这会引发其他安全问题。不幸的是,干净的修复需要修改 API。

您说使用 IP 地址作为 CN 是一种黑客行为,但实际上将其用作 ipAddress SubjectAlternativeName 是完全有效的。

安全问题来自事实上,httpclient 尝试匹配 dns 生成的数据(反向查找,现在还解析了主机名),而不是用户实际键入的内容,从而导致 DNS 中毒或连接重定向攻击。

第一种情况:
- 用户想要连接到 1.2.3.4
- DNS 反向查找是 xxx.yyy.zzz
- 恶意代理将连接重定向到服务器 4.3.2.1
- 服务器证书包含设置为 xxx.yyy 的 CN 或 SAN。 zzz
- 一切正常(但不应该)

第二种情况:
- 用户想要连接到 xxx.yyy.zzz
- 被黑客入侵的 DNS 错误地将其解析为 1.2.3.4- 服务器证书的 CN 或 SAN 设置为 1.2.3.4
- 连接建立正常(但显然不应该)

。很公平。我将恢复补丁并以 WONTFIX

Oleg 的身份关闭问题。您描述的第一个场景还需要来自火星的绿人以及发送客户端信任的证书的恶意 4.3.2.1 服务器的参与才能实用。

Oleg', '', '''') as m) 到 '/tmp/csv-temp/BUG/httpclient-HTTPCLIENT-1051.txt' 上下文:函数 PL/pgSQL generate_mallet_input2() linha 31 em comando EXECUTE ********** 错误**********

ERRO: erro de sintaxe em ou próximo a "mailto" SQL 状态:42601 上下文:函数 PL/pgSQL generate_mallet_input2() linha 31 em comando EXECUTE

检索到的内容是软件仓库中关于项目问题的长文本,该文本中可以有html。导致问题的原因是 HTML 引号。

【问题讨论】:

    标签: sql database postgresql postgresql-9.3


    【解决方案1】:

    需要转义的不是字符串的内容,而是它在您发送到服务器的SQL中的表示

    为了表示单个',您需要在SQL 语法中编写两个:''。所以,'IMSoP''s answer' 代表字符串IMSoP's answer'''' 代表''''''' 代表''

    但关键是您需要在尝试运行 SQL 之前执行此操作。您不能将无效的 SQL 命令粘贴到查询窗口中并告诉它自行修复。

    因此,转义的自动化完全取决于您如何创建该 SQL。根据您更新的问题,我们现在知道您正在使用 pl/pgsql 创建 SQL 语句,在此 format() 调用中:

    format('Copy( select REPLACE(''%s'', '''', '''''''') as m ) To ''/tmp/csv-temp/BUG/'|| quote_ident(sch) || '-' || r.id::text || '.txt ''',r.mess)
    

    让我们稍微简化一下以使示例更清晰:

    format('select REPLACE(''%s'', '''', '''''''') as m', r.mess)
    

    如果r.messfoo,结果将如下所示:

    select REPLACE('foo', '', ''''') as m
    

    这个替换不会做任何有用的事情,因为第一个参数是一个空字符串,第二个有 3 个' 标记;但即使你修复了' 标记的数量,它也不起作用。如果r.mess 的值改为bad'stuff,你会得到:

    select REPLACE('bad'stuff', '', ''''') as m
    

    这是无效的 SQL;不管你在哪里运行,它都不起作用,因为 Postgres 认为 'bad' 是一个字符串,而接下来的 stuff 是无效的语法。

    想想如果r.messSQL injection'); DROP TABLE users -- 会是什么样子:

    select REPLACE('SQL injection'); DROP TABLE users; --', '', ''''') as m
    

    现在我们有了有效的 SQL,但它可能不是您想要的!

    所以你需要做的是在r.mess中转义'标记你把它混合到字符串中:

    format('select '%s' as m', REPLACE(r.mess, '''', ''''''))
    

    现在我们在将 bad'stuff 更改为 bad''stuff 之前,它会进入 SQL,并以以下方式结束:

    select 'bad''stuff' as m
    

    这就是我们想要的。

    不过,实际上有一些更好的方法可以做到这一点:

    the format function 使用%L 修饰符,它会输出一个转义和引用的字符串文字:

    format('select %L as m', r.mess)
    

    使用quote_literal() or quote_nullable() string functions 而不是replace(),并像使用文件名一样将字符串连接在一起:

    'select ' || quote_literal(r.mess) || ' as m'
    

    最后,如果函数看起来真的和你的问题一样,你可以完全不使用循环来避免整个问题;只需使用适当的WHERE 子句将每组行复制到文件中:

     EXECUTE 'Copy
         SELECT concat(rp.summary,rp.description,string_agg(c.message, ''. '')) as mess
            FROM ' || sch || '.report rp
            INNER JOIN ' || sch || '.report_comment rc ON rp.id=rc.report_id
            INNER JOIN ' || sch || '.comment c ON rc.comments_generatedid=c.generatedid 
            INNER JOIN ' || sch || '.gold_set g ON rp.id=g.key
            WHERE g.classified = ''BUG'' -- <-- Note changed WHERE clause
            GROUP BY g.classified,rp.summary,rp.description,rp.id
    ) To ''/tmp/csv-temp/BUG/'|| quote_ident(sch) || '-' || r.id::text || '.txt '''
    ';
    

    重复IMPROVEMENTREFACTORING。我不能确定,但​​总的来说,一次对一组行进行操作比在它们上循环更有效。在这里,您必须进行 3 次查询,但您原始版本中的 = any() 可能效率很低。

    【讨论】:

    • 我在 pgAdmin IDE 中代表 plpgsql。 @IMSoP
    【解决方案2】:

    既然我想我知道你在问什么,我正在尝试解决这个问题。

    您在表格中有一个字段,当您运行 SELECT &lt;field&gt; from &lt;table&gt; 时,您将返回结果:

    'This'is'a'test'
    

    你想要,intead,这个结果看起来像:

    'This''is''a''test'
    

    所以:

    CREATE Table test( testfield varchar(30));
    INSERT INTO test VALUES ('''This''is''a''test''');
    

    你可以运行:

    SELECT 
      '''' || REPLACE(Substring(testfield FROM 2 FOR LENGTH(testfield) - 2),'''', '''''') || '''' 
    FROM Test;
    

    这将只获取第一个和最后一个单引号内的位,然后它将用双引号替换内部的单引号。最后,它将单引号连接到开头和结尾。

    SQL 小提琴: http://sqlfiddle.com/#!15/a99e6/4

    如果您在字符串结果的内部寻找的不是双单引号,那么您可以将 REPLACE() 函数更改为适当的字符。此外,如果您要查找的不是单引号来封装字符串,那么您可以使用串联来更改它们。

    【讨论】:

      【解决方案3】:

      您需要转义引用的' 标记:

      select 'hjhjjjhjh''mnmnmnm''mn'
      

      【讨论】:

      • 如何自动执行此操作?我需要在 select 语句中的表中实现文本列。 @Flimzy
      • 我不确定你所说的自动是什么意思。
      • 看起来像:选择 REGEXP_REPLACE('hjhjjjhjh'mnmnmnm'mn', '\\''+', '''', 'g')。但它没有用。 @Flimzy
      • @TarcisioM.:所以,正如我所说,您需要转义您的 ' 字符。
      猜你喜欢
      • 1970-01-01
      • 2019-04-22
      • 1970-01-01
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-05
      • 1970-01-01
      相关资源
      最近更新 更多