Java-SQL 삽입을 방지하는 이스케이프 문자열


146

자바에서 안티 SQL 주입을 시도하고 있는데 "replaceAll"문자열 함수로 작업하기가 매우 어렵다는 것을 알게되었습니다. 궁극적으로 내가 기존 변환하는 기능을 필요 \\\있는, "\", 어떤 '대상을 \', 어떤 \n\\n너무 문자열이 MySQL의 SQL 주입에 의해 평가 될 때이 차단됩니다.

내가 일하고있는 코드를 훔 쳤고 \\\\\\\\\\\함수의 모든 것이 내 눈을 멀게합니다. 누구든지 이것에 대한 예가 있다면 크게 감사하겠습니다.


1
좋아, 나는 PreparedStatements가 갈 길이라는 결론에 도달했지만, 현재 계획에 따라 진행 해야하는 현재 방해 요인을 바탕으로 한 순간 동안 필터를 배치하고 현재 이정표에 도달하면 할 수 있습니다. 다시 돌아가서 준비된 진술을 위해 데이터베이스를 리팩터링하십시오. 그 동안 모멘텀을 유지하기 위해 누군가 Java를 고려할 때 MySQL에 대해 위의 문자를 효과적으로 이스케이프 처리하는 솔루션이 있습니까? 정규 표현식 시스템은 필요한 이스케이프 횟수를 해결하는 데 절대적인 고통입니다 ....
Scott Bonner

2
"SET ROLE role_name"또는 "LISTEN channel_name"과 같이 모든 SQL 문을 매개 변수화 할 수있는 것은 아닙니다.
Neil McGuigan

1
트윗 담아 가기 매개 변수화 하려는 부분이 실제로 DML 이지만 CREATE VIEW myview AS SELECT * FROM mytable WHERE col = ?주요 문은 DDL 문이므로 대부분의 드라이버는 매개 변수화를 거부합니다 .
SeldomNeedy

답변:


249

PreparedStatement는 SQL 삽입을 불가능하게하기 때문에 갈 길입니다. 다음은 사용자 입력을 매개 변수로 사용하는 간단한 예입니다.

public insertUser(String name, String email) {
   Connection conn = null;
   PreparedStatement stmt = null;
   try {
      conn = setupTheDatabaseConnectionSomehow();
      stmt = conn.prepareStatement("INSERT INTO person (name, email) values (?, ?)");
      stmt.setString(1, name);
      stmt.setString(2, email);
      stmt.executeUpdate();
   }
   finally {
      try {
         if (stmt != null) { stmt.close(); }
      }
      catch (Exception e) {
         // log this error
      }
      try {
         if (conn != null) { conn.close(); }
      }
      catch (Exception e) {
         // log this error
      }
   }
}

이름과 이메일에있는 문자가 무엇이든 해당 문자는 데이터베이스에 직접 배치됩니다. 어떤 식 으로든 INSERT 문에 영향을 미치지 않습니다.

각기 다른 데이터 유형에 대해 서로 다른 설정 방법이 있습니다. 사용하는 방법은 데이터베이스 필드에 따라 다릅니다. 예를 들어, 데이터베이스에 INTEGER 열이 있으면 setInt메소드를 사용해야합니다 . PreparedStatement 문서 에는 데이터 설정 및 가져 오기에 사용할 수있는 다양한 방법이 모두 나열되어 있습니다.


1
이 방법을 통해 모든 매개 변수를 문자열로 취급하고 여전히 안전합니까? 나는 ... 전체 데이터베이스 계층을 재 구축 할 필요없이 안전을 위해 기존 아키텍처를 업데이트하는 방법을 알아 내려고 노력하고있어
스콧 보너

1
모든 dynqmic SQL은 단지 문자열이므로 질문 할 필요는 없습니다. PrepareStatement에 익숙하지 않기 때문에 실제 질문은 매개 변수화 된 쿼리를 생성 한 다음 ExecuteUpdate로 실행할 수 있다는 것입니다. 그렇다면, 좋습니다. 그렇지 않다면 단순히 문제를 숨기고있는 것이므로 데이터베이스 계층을 다시 디자인하는 것 외에는 안전한 옵션이 없을 수 있습니다. SQL 주입을 다루는 것은 처음부터 디자인해야 할 것들 중 하나입니다. 나중에 쉽게 추가 할 수있는 것이 아닙니다.
Cylon Cat

2
INTEGER 필드에 삽입하는 경우 'setInt'를 사용하려고합니다. 마찬가지로 다른 숫자 데이터베이스 필드는 다른 세터를 사용합니다. 모든 세터 유형을 나열하는 PreparedStatement 문서에 대한 링크를 게시했습니다.
Kaleb Brasee

2
예 Cylon, PreparedStatements는 매개 변수화 된 쿼리를 생성합니다.
Kaleb Brasee

2
@Kaleb Brasee, 감사합니다. 알아두면 좋습니다. 도구는 환경에 따라 다르지만 매개 변수가있는 쿼리를 얻는 것이 근본적인 답변입니다.
Cylon Cat

46

SQL 삽입을 방지하는 유일한 방법은 매개 변수화 된 SQL을 사용하는 것입니다. 생계를 위해 SQL을 해킹하는 사람들보다 똑똑한 필터를 구축하는 것은 불가능합니다.

따라서 모든 입력, 업데이트 및 where 절에 매개 변수를 사용하십시오. 동적 SQL은 해커에게 개방 된 문이며 저장 프로 시저의 동적 SQL을 포함합니다. 파라미터 화, 파라미터 화, 파라미터 화.


11
심지어 매개 변수화 된 SQL조차도 100 % 보장되지 않습니다. 그러나 아주 좋은 시작입니다.
duffymo

2
@ duffymo, 나는 아무것도 100 % 안전하다는 데 동의합니다. 매개 변수화 된 SQL에서도 작동하는 SQL 주입의 예가 있습니까?
Cylon Cat

3
@Cylon Cat : @WhereClause 또는 @tableName과 같은 SQL 청크가 매개 변수로 전달되고 SQL에 연결되어 동적으로 실행될 때 물론입니다. 사용자가 코드를 작성할 수있게하면 SQL 삽입이 발생합니다. 코드를 매개 변수로 캡처하는지 여부는 중요하지 않습니다.
Steve Kass

16
이 더 언급되지 않는 이유 BTW, 나도 몰라,하지만 PreparedStatement들과 함께 작업하는도 훨씬 쉽고 훨씬 더 읽기. 그것만으로도 그것들을 아는 모든 프로그래머에게 디폴트가 될 것입니다.
Edan Maor

3
일부 데이터베이스의 PreparedStatements는 작성하는 데 비용이 매우 많이들므로 많은 작업을 수행해야하는 경우 두 유형을 모두 측정하십시오.
Thorbjørn Ravn Andersen 08

36

실제로 방어 옵션 1 : 준비된 문 (매개 변수화 된 쿼리) 또는 방어 옵션 2 : 저장 프로 시저 를 사용할 수없는 경우 자체 도구를 작성하지 말고 OWASP Enterprise Security API를 사용하십시오 . Google 코드에서 호스팅 되는 OWASP ESAPI 에서 :

자신의 보안 통제를 작성하지 마십시오! 모든 웹 애플리케이션 또는 웹 서비스에 대한 보안 제어를 개발할 때 바퀴를 재창조하면 시간 낭비와 막대한 보안 허점이 발생합니다. ESWA (Enterprise Security API) 툴킷은 소프트웨어 개발자가 보안 관련 설계 및 구현 결함을 방지하도록 도와줍니다.

자세한 내용 은 Java 에서 SQL 주입 방지SQL 주입 방지 치트 시트를 참조하십시오 .

방어 옵션 3 : OWASP ESAPI 프로젝트 를 소개하는 모든 사용자 제공 입력 탈출에 특별한주의를 기울이십시오 ).


4
ESAPI는 현재로서는 기능이 없어 보입니다. AWS에는 SQL 주입, XSS 등을 방지 할 수있는 WAF가 있습니다.이 시점에서 다른 대안이 있습니까?
ChrisOdney

@ChrisOdney WAF를 쉽게 우회 할 수 있습니다. 대부분의 프레임 워크는 이미 자체적으로 매개 변수를 이스케이프하는 자체 SQL 주입 방지 기능을 구현합니다. 레거시 프로젝트의 대안 : owasp.org/index.php/…
Javan R.

19

(이것은 원래 질문에 대한 OP의 의견에 대한 답변입니다. 나는 PreparedStatement가 정규식이 아닌이 작업을위한 도구라는 것에 완전히 동의합니다.)

말할 때 \n시퀀스 \+ n또는 실제 줄 바꿈 문자를 의미합니까? 그것의 경우 \+ n, 작업은 매우 간단합니다 :

s = s.replaceAll("['\"\\\\]", "\\\\$0");

입력에서 하나의 백 슬래시를 일치 시키려면 4 개를 정규식 문자열에 넣습니다. 하나의 백 슬래시를 출력에 넣으려면 그 중 4 개를 대체 문자열에 넣습니다. 이것은 Java 문자열 리터럴 형식으로 정규식 및 대체를 작성한다고 가정합니다. 다른 방법으로 (예 : 파일에서 읽음) 작성하면 이중 이스케이프를 모두 수행 할 필요는 없습니다.

입력에 줄 바꿈 문자가 있고 이스케이프 시퀀스로 바꾸려면 다음을 사용하여 입력을 다시 통과시킬 수 있습니다.

s = s.replaceAll("\n", "\\\\n");

또는 두 개의 백 슬래시를 원할 수도 있습니다 (나는 그것에 대해 명확하지 않습니다).

s = s.replaceAll("\n", "\\\\\\\\n");

1
의견 주셔서 감사합니다, 나는 당신이 하나의 모든 문자를 수행 한 방식을 좋아합니다. 나는 각각에 대해 모두를 대체하는 덜 정규적인 표현 방식에 대해 가고있었습니다 ...이 질문에 대한 답변을 지금 어떻게 할당 해야할지 모르겠습니다. . 궁극적으로 PreparedStatements가 답이지만, 현재 목표에 대한 답은 내가 필요로하는 답입니다. 이전에 준비된 진술의 답 중 하나에 대한 답을 주었을 때 화가 났습니까? 아니면 부부 사이에서 답을 공유 할 방법이 있습니까?
Scott Bonner

1
이것은 일시적인 문제이므로 계속 진행하여 PreparedStatement 답변 중 하나를 수락하십시오.
Alan Moore

12

PreparedStatement는 대부분의 경우 진행되는 방법이지만 모든 경우에 적용되는 것은 아닙니다. 때로는 쿼리 또는 쿼리의 일부를 나중에 사용하기 위해 문자열로 작성하여 저장해야하는 상황에 처할 수도 있습니다. 다른 프로그래밍 언어의 API 및 자세한 내용 은 OWASP 사이트 에서 SQL Injection Prevention Cheat Sheet 를 참조하십시오.


1
OWASP 치트 시트가 GitHub로 이동되었습니다. SQL 인젝션 치트 시트는 다음과 같습니다 : github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/…
theferrit32

10

준비된 문장이 가장 좋은 솔루션이지만 실제로 수동으로해야하는 경우 StringEscapeUtilsApache Commons-Lang 라이브러리 의 클래스를 사용할 수도 있습니다 . escapeSql(String)사용할 수 있는 방법이 있습니다.

import org.apache.commons.lang.StringEscapeUtils; … String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);


2
참조 : commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/… 어쨌든이 방법은 따옴표 만 이스케이프하고 SQL 인젝션 공격을 막지 않는 것 같습니다.
Paco Abato

11
그것은 단지 작은 따옴표를 탈출했기 때문에 이것은 최신 버전에서 제거
피니 Cheyni에게

2
이 답변은 SQL 삽입을 막지 않기 때문에 삭제해야합니다.
Javan R.

9

정규식을 사용하여 텍스트를 제거하면 SQL 문이 SQL 문이 Statement아닌을 통해 데이터베이스로 전송되는 것처럼 들립니다 PreparedStatement.

처음에 SQL 삽입을 방지하는 가장 쉬운 방법 중 하나는 PreparedStatement문자열을 연결하여 데이터베이스에 전송할 SQL 문을 작성하지 않는 플레이스 홀더를 사용하여 데이터를 SQL 문으로 대체 할 수있는을 사용하는 것입니다.

자세한 내용은 문 준비 사용 에서 자바 튜토리얼 시작하기에 좋은 장소가 될 것입니다.


6

레거시 시스템을 다루거나 PreparedStatement너무 짧은 시간에 전환하기에 너무 많은 장소 가있는 경우, 즉 다른 답변에서 제안한 모범 사례를 사용하는 데 장애가있는 경우 AntiSQLFilter 를 사용해 볼 수 있습니다


5

아래 코드가 필요합니다. 한눈에, 이것은 내가 만든 오래된 코드처럼 보일 수 있습니다. 그러나 내가 한 것은 http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.31/com/mysql/jdbc/PreparedStatement 의 소스 코드를 살펴 보는 것 입니다. 자바 . 그런 다음 setString (int parameterIndex, String x) 코드를 신중하게 조사하여 이스케이프 된 문자를 찾아 내 클래스에 맞게 사용자 정의하여 필요한 목적으로 사용할 수 있도록했습니다. 결국, 이것이 Oracle이 이스케이프 처리하는 문자 목록 인 경우, 이것이 보안 측면에서 실제로 위안이된다는 것을 아는 것입니다. 아마도 오라클은 차기 주요 Java 릴리스를 위해 이와 유사한 방법을 추가 할 필요가있을 것입니다.

public class SQLInjectionEscaper {

    public static String escapeString(String x, boolean escapeDoubleQuotes) {
        StringBuilder sBuilder = new StringBuilder(x.length() * 11/10);

        int stringLength = x.length();

        for (int i = 0; i < stringLength; ++i) {
            char c = x.charAt(i);

            switch (c) {
            case 0: /* Must be escaped for 'mysql' */
                sBuilder.append('\\');
                sBuilder.append('0');

                break;

            case '\n': /* Must be escaped for logs */
                sBuilder.append('\\');
                sBuilder.append('n');

                break;

            case '\r':
                sBuilder.append('\\');
                sBuilder.append('r');

                break;

            case '\\':
                sBuilder.append('\\');
                sBuilder.append('\\');

                break;

            case '\'':
                sBuilder.append('\\');
                sBuilder.append('\'');

                break;

            case '"': /* Better safe than sorry */
                if (escapeDoubleQuotes) {
                    sBuilder.append('\\');
                }

                sBuilder.append('"');

                break;

            case '\032': /* This gives problems on Win32 */
                sBuilder.append('\\');
                sBuilder.append('Z');

                break;

            case '\u00a5':
            case '\u20a9':
                // escape characters interpreted as backslash by mysql
                // fall through

            default:
                sBuilder.append(c);
            }
        }

        return sBuilder.toString();
    }
}

2
이 코드는 위의 링크에서 소스 코드의 디 컴파일 된 버전이라고 생각합니다. 더 최근 mysql-connector-java-xxxcase '\u00a5'case '\u20a9'진술은 제거 된 것으로 보인다
zhy

코드로 sqlmap을 시도했지만 frist 공격으로부터 나를 보호하지 못했습니다`유형 : 부울 기반 블라인드 제목 : AND 부울 기반 블라인드-WHERE 또는 HAVING 절 페이로드 : q = 1 % 'AND 5430 = 5430 AND'% ' = '`
shareef

미안의 작동하지만 마지막 저장된 세션 결과를보고했다 ... 나는 비슷한 미래 ..에 대한 의견 유지
샤리프

당신은 사용할 수 있습니다 org.ostermiller.utils.StringHelper.escapeSQL()또는 com.aoindustries.sql.SQLUtility.escapeSQL().
Mohamed Ennahdi El Idrissi

1
원본 코드에 대한 GPLv2 라이센스는이 코드를 사용하는 모든 사람을 위해 복사 한 것입니다. 저는 변호사가 아니지만 라이센스가있는 코드를 포함시키는 의미를 완전히 알고 있지 않으면 프로젝트에서이 답변을 사용하지 않는 것이 좋습니다.
Nick Spacek

0

레거시 시스템의 경우 모든 위치에서 준비된 통계를 적용 할 수없는 경우, sqlmap이 SQL을 주입하지 못하도록 테스트 솔루션을 많이 검색 한 후.

java-security-cross-site-scripting-xss-and-sql-injection 주제 해결 방법

@Richard 솔루션을 시도했지만 내 경우에는 작동하지 않았습니다. 나는 필터를 사용했다

이 필터의 목표는 요청을 자체 코드화 된 랩퍼 MyHttpRequestWrapper로 랩핑하여 다음을 변환하는 것입니다.

org.springframework.web.util.HtmlUtils.htmlEscape (…) 메소드를 통해 HTML 코드에 특수 문자 (<,>, ',…)가있는 HTTP 매개 변수 참고 : Apache Commons에는 비슷한 클래스가 있습니다. org.apache.commons.lang.StringEscapeUtils.escapeHtml (…) Apache Commons 클래스 org.apache.commons.lang.StringEscapeUtils를 통한 SQL 삽입 문자 ( ',“,…). escapeSql (…)

<filter>
<filter-name>RequestWrappingFilter</filter-name>
<filter-class>com.huo.filter.RequestWrappingFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>RequestWrappingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>




package com.huo.filter;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletReponse;
import javax.servlet.http.HttpServletRequest;

public class RequestWrappingFilter implements Filter{

    public void doFilter(ServletRequest req, ServletReponse res, FilterChain chain) throws IOException, ServletException{
        chain.doFilter(new MyHttpRequestWrapper(req), res);
    }

    public void init(FilterConfig config) throws ServletException{
    }

    public void destroy() throws ServletException{
    }
}




package com.huo.filter;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.apache.commons.lang.StringEscapeUtils;

public class MyHttpRequestWrapper extends HttpServletRequestWrapper{
    private Map<String, String[]> escapedParametersValuesMap = new HashMap<String, String[]>();

    public MyHttpRequestWrapper(HttpServletRequest req){
        super(req);
    }

    @Override
    public String getParameter(String name){
        String[] escapedParameterValues = escapedParametersValuesMap.get(name);
        String escapedParameterValue = null; 
        if(escapedParameterValues!=null){
            escapedParameterValue = escapedParameterValues[0];
        }else{
            String parameterValue = super.getParameter(name);

            // HTML transformation characters
            escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);

            // SQL injection characters
            escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);

            escapedParametersValuesMap.put(name, new String[]{escapedParameterValue});
        }//end-else

        return escapedParameterValue;
    }

    @Override
    public String[] getParameterValues(String name){
        String[] escapedParameterValues = escapedParametersValuesMap.get(name);
        if(escapedParameterValues==null){
            String[] parametersValues = super.getParameterValues(name);
            escapedParameterValue = new String[parametersValues.length];

            // 
            for(int i=0; i<parametersValues.length; i++){
                String parameterValue = parametersValues[i];
                String escapedParameterValue = parameterValue;

                // HTML transformation characters
                escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);

                // SQL injection characters
                escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);

                escapedParameterValues[i] = escapedParameterValue;
            }//end-for

            escapedParametersValuesMap.put(name, escapedParameterValues);
        }//end-else

        return escapedParameterValues;
    }
}

좋은가요 java-security-cross-site-scripting-xss-and-sql-injection topic? 레거시 응용 프로그램에 대한 솔루션을 찾으려고합니다.
caot

0

보낸 사람 : [출처]

public String MysqlRealScapeString(String str){
  String data = null;
  if (str != null && str.length() > 0) {
    str = str.replace("\\", "\\\\");
    str = str.replace("'", "\\'");
    str = str.replace("\0", "\\0");
    str = str.replace("\n", "\\n");
    str = str.replace("\r", "\\r");
    str = str.replace("\"", "\\\"");
    str = str.replace("\\x1a", "\\Z");
    data = str;
  }
return data;

}


0

PL / SQL을 사용하는 DBMS_ASSERT 경우 입력을 소독하여 SQL 삽입에 대해 걱정하지 않고 사용할 수 있습니다.

예를 들어이 답변을 참조하십시오 : https : //.com/a/21406499/1726419

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.