select 语句中文本内的单引号错误

Error with single quotes inside text in select statement

使用 Postgresql 9.3 时出现错误:

select 'hjhjjjhjh'mnmnmnm'mn'

错误:

ERRO:syntax error in or next to "'mn'"
SQL state: 42601
Character: 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: erro de sintaxe em ou próximo a "mailto" LINHA 1: ...e.http.impl.conn.SingleClientConnManager$HTTPCLIENT-803).
The new SSLSocketFactory.connectSocket method calls the X509HostnameVerifier with InetSocketAddress.getHostName() parameter. When the selected IP address has a reverse lookup name, the verifier is called with the resolved name, and so the IP check fails.
4.0 release checked for original ip/hostname, but this cannot be done with the new connectSocket() method.
The TestHostnameVerifier.java only checks 127.0.0.1/.2 and so masked the issue, because the matching certificate has both "localhost" and "127.0.0.1", but actually only "localhost" is matched. A test case with 8.8.8.8 would be better.I committed a slightly better workaround for the problem that does not require reverse DNS lookups.

Oleg. I had to resort to a fairly ugly hack in order to fix the problem. A better solution would require changes to the X509HostnameVerifier API. I felt that deprecation of the X509HostnameVerifier interface was not warranted, as the use of an IP address for CN in a certificate was a hack by itself.

Please review.

Oleg . Even the second one requires the server presenting a trusted certificate. I don't see much difference beetween the two cases.. Wrong test. Try to connect to https://93.62.162.60:8443/. The certificate has CN=93.62.162.60, but the check is done for 93-62-162-60.ip23.fastwebnet.it. Hmm, my comment was not meant to revert the patch. The first scenario was already exploitable and still is. Your patch is the "correct" solution without breaking the API.

But to avoid any security issue (including the ones already present) the API have to be changed.. I am not able to reproduce the problem. SSL connections to remote peers pass the default host name verification.

---
executing requestGET https://www.verisign.com/ HTTP/1.1
[DEBUG] SingleClientConnManager - Get connection for route HttpRoute[{s}->https://www.verisign.com]
[DEBUG] DefaultClientConnectionOperator - Connecting to www.verisign.com/69.58.181.89:443
[DEBUG] RequestAddCookies - CookieSpec selected: best-match
[DEBUG] DefaultHttpClient - Attempt 1 to execute request
[DEBUG] DefaultClientConnection - Sending request: GET / HTTP/1.1
[DEBUG] headers - >> GET / HTTP/1.1
[DEBUG] headers - >> Host: www.verisign.com
[DEBUG] headers - >> Connection: Keep-Alive
[DEBUG] headers - >> User-Agent: Apache-HttpClient/4.1 (java 1.5)
[DEBUG] DefaultClientConnection - Receiving response: HTTP/1.1 200 OK
[DEBUG] headers - << HTTP/1.1 200 OK
[DEBUG] headers - << Date: Thu, 03 Feb 2011 20:14:35 GMT
[DEBUG] headers - << Server: Apache
[DEBUG] headers - << Set-Cookie: v1st=D732270AE4FC9F76; path=/; expires=Wed, 19 Feb 2020 14:28:00 GMT; domain=.verisign.com
[DEBUG] headers - << Set-Cookie: v1st=D732270AE4FC9F76; path=/; expires=Wed, 19 Feb 2020 14:28:00 GMT; domain=.verisign.com
[DEBUG] headers - << X-Powered-By: PHP/5.2.13
[DEBUG] headers - << Keep-Alive: timeout=5, max=100
[DEBUG] headers - << Connection: Keep-Alive
[DEBUG] headers - << Transfer-Encoding: chunked
[DEBUG] headers - << Content-Type: text/html
[DEBUG] ResponseProcessCookies - Cookie accepted: "[version: 0][name: v1st][value: D732270AE4FC9F76][domain: .verisign.com][path: /][expiry: Wed Feb 19 15:28:00 GMT+01:00 2020]".
[DEBUG] ResponseProcessCookies - Cookie accepted: "[version: 0][name: v1st][value: D732270AE4FC9F76][domain: .verisign.com][path: /][expiry: Wed Feb 19 15:28:00 GMT+01:00 2020]".
[DEBUG] DefaultHttpClient - Connection can be kept alive for 5000 MILLISECONDS
----------------------------------------
HTTP/1.1 200 OK
Response content length: -1
[DEBUG] SingleClientConnManager - Releasing connection org.apache.http.impl.conn.SingleClientConnManager$ConnAdapter@15ad5c6
[DEBUG] DefaultClientConnection - Connection shut down
---

Are you using a custom SSL socket factory by any chance? Does it implement the LayeredSchemeSocketFactory interface?

Oleg. Great work, good patch, thanks!. Well, I looked at the patch. It should fix the issue (though not completely, since the reverse lookup could give a wrong/unresolvable hostname), but as you said it's a crude hack, and this opens to other security issues. Unfortunately the clean fix requires API modification.

You say using an IP address as CN is a hack, but actually using it as an ipAddress SubjectAlternativeName is perfectly valid.

The security issues arise from the fact that httpclient tries to match dns generated data (reverse lookups and now also resolved hostnames) instead of what the user actually typed, opening to DNS poisoning or connection redirect attacks.

First scenario:
- user wants to connect to 1.2.3.4
- DNS reverse lookup is xxx.yyy.zzz
- a malicious proxy redirects the connection to server 4.3.2.1
- server certificate contains CN or SAN set to xxx.yyy.zzz
- All OK (but shouldn't)

Second scenario:
- user wants to connect to xxx.yyy.zzz
- hacked DNS incorrectly resolve it to 1.2.3.4
- server certificate has CN or SAN set to 1.2.3.4
- The connection is established OK (but clearly shouldn't)

. Fair enough. I'll revert the patch and close the issue as WONTFIX

Oleg. The first scenario you are describing would also require involvement of green men from Mars and the malicious 4.3.2.1 server sending a certificate trusted by the client to be practical.

Oleg', '', '''') as m ) To '/tmp/csv-temp/BUG/httpclient-HTTPCLIENT-1051.txt ' CONTEXTO: função PL/pgSQL generate_mallet_input2() linha 31 em comando EXECUTE ********** Error **********

ERRO: erro de sintaxe em ou próximo a "mailto" SQL state: 42601 Context: função PL/pgSQL generate_mallet_input2() linha 31 em comando EXECUTE

检索到的内容是关于软件存储库中项目问题的长文本,并且可以在该文本中包含 html。 Html 引号导致了问题。

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

select 'hjhjjjhjh''mnmnmnm''mn'

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

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

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

因此,转义的自动化完全取决于您如何创建 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() 可能效率很低。

我现在正在尝试这个,因为我想我知道你在问什么。

您在 table 中有一个字段,当您 运行 SELECT <field> from <table> 返回结果时:

'This'is'a'test'

您希望这个结果看起来像:

'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 Fiddle: http://sqlfiddle.com/#!15/a99e6/4

如果您在字符串结果内部查找的不是双单引号,则可以将 REPLACE() 函数更改为适当的字符。另外,如果您要封装字符串的不是单引号,那么您可以通过连接来更改它们。