LINE Corporation이 2023년 10월 1일부로 LY Corporation이 되었습니다. LY Corporation의 새로운 기술 블로그를 소개합니다. LY Corporation Tech Blog

Blog


자바 애플리케이션의 보안 검수 방법 살펴보기

안녕하세요. LINE에서 보안 업무를 담당하고 있는 최하늘입니다. LINE은 메신저 이외에도 많은 서비스를 론칭하고 있고 각 서비스는 새로운 기능이 추가되거나 버그가 수정될 때마다 수시로 업데이트됩니다. 보안 팀에선 새로 출시한 서비스나 업데이트한 서비스에 보안 상 문제가 없는지 확인하는데요. 이때 기존 코드에 이미 수행했던 동일한 보안 평가를 서비스에 새로 추가된 코드에 반복적으로 수행해야 하는 경우가 많았습니다. 그래서 보안 팀에선 자주 발견되는 보안 취약점 패턴을 정의하는 등 취약점을 자동으로 탐지해내기 위해 끊임없이 노력하고 있는데요. 이와 더불어 개발자들이 개발 과정에서 취약점을 자동으로 발견할 수 있는 도구를 사용한다면 보안 평가 과정에서 발견되는 보안 이슈가 훨씬 줄어들 수 있을 것입니다. 실제로 이를 위한 오픈소스나 상용 제품이 존재하며 LINE에서도 이를 적용하여 애플리케이션의 보안 이슈를 사전에 제거하려고 노력하고 있습니다. 이번 글에서는 보안 취약점을 자동으로 찾아내는 방법 중의 하나인 정적 분석에 대해서 알아보고, 자바(Java)용 오픈소스 정적 분석 도구인 SpotBugs를 보안 평가에 어떻게 활용할 수 있는지 알아보겠습니다.

정적 분석이란?

정적 분석과 동적 분석

프로그램의 보안 취약점 분석 방식은 크게 정적 분석과 동적 분석으로 나뉩니다. 정적 분석은 프로그램을 실행하지 않은 상태에서 소스 코드나 컴파일된 코드를 이용해 프로그램을 분석하는 방법이며, 동적 분석은 프로그램을 실제 환경이나 가상 환경에서 실행해 보면서 분석하는 방법입니다. 정적 분석은 소스 코드의 모든 부분을 확인할 수 있지만, 실행 환경에서의 상태를 정확히 알 수 없기 때문에 실행할 때에만 알 수 있는 데이터가 필요한 경우엔 정확히 분석할 수 없습니다. 반대로 동적 분석은 실제로 실행해 보면서 분석하기 때문에 실행 환경에서의 상태를 잘 알 수 있지만, 프로그램을 실행할 수 있는 환경을 구축하기 어려울 때가 많고 소스 코드의 모든 부분을 테스트해 보기 힘들다는 문제가 있습니다. 이렇게 각 분석 방식은 장단점이 존재하기 때문에 정적 분석은 주로 개발 단계에서 소스 코드의 구조적인 문제나 실수를 찾아내는 데 사용하며 동적 분석은 테스트나 모니터링할 때 사용합니다(개발 과정 중에 작성하는 유닛 테스트를 동적 분석 방법의 한 가지로 볼 수 있습니다). 표로 정리하면 다음과 같습니다.

정적 분석(static analysis) 동적 분석(dynamic analysis)
분석 대상 소스 코드 또는 컴파일된 코드 프로그램 실행 환경
테스트 범위 소스 코드의 모든 부분 실행 가능한 경로
활용 코드 상의 문제나 실수를 찾음 테스트, 모니터

Taint 분석

정적 분석 도구의 사용법을 이해하는 데 도움이 되는 'Taint 분석'에 대한 설명을 먼저 드리겠습니다. Taint 분석은 정적이나 동적으로 프로그램을 분석할 때 모두 사용할 수 있는 방법 중의 하나로, 프로그램의 흐름에 따라 사용자의 입력을 추적하면서 분석하는 방법입니다.

Taint 분석 시 알아두면 좋은 용어가 두 개 있는데요. 바로 'taint source'와 'sink'입니다. 사용자의 입력으로 만들어진 값을 오염(taint)된 값이라고 하고 이를 taint source라고 표현합니다. 그리고 taint source가 인자로 주어졌을 때 문제가 발생하는 함수를 sink라고 합니다. 예를 들어 자바에서는 파일을 열 수 있는 java.io.File() 메서드나 임의의 명령을 실행할 수 있는 java.lang.Runtime.exec() 메서드가 sink입니다. 동적 분석에서 Taint 분석을 사용할 때는 프로그램에 실제 입력되는 값을 taint source로 지정하고 프로그램에서 해당 입력 값을 다루는 부분부터 추적합니다. 하지만 정적 분석에서 Taint 분석을 사용할 때는 사용자의 입력을 정확히 정의하기 힘들기 때문에 taint source를 임의로 정하여 분석을 진행합니다. 자세한 내용을 예제와 함께 살펴보겠습니다.

SpotBugs와 Find Security Bugs 플러그인

자바 애플리케이션의 정적 분석을 도와주는 도구는 오픈 소스부터 상용 제품까지 굉장히 다양합니다. 그중에서 SpotBugs라는 오픈소스 정적 분석 도구를 사용해 보겠습니다.

SpotBugs는 자바용 오픈소스 정적 분석 도구입니다. 2004년에 메릴랜드 대학(University of Maryland)에서 FindBugs라는 이름으로 시작되어 SpotBugs로 이름이 바뀌었고, 현재까지도 개발이 활발하게 진행되고 있습니다. SpotBugs는 바이트코드로 컴파일된 자바 소스 코드를 분석하는 방식으로 만들어졌기 때문에 소스 파일이 없는 외부 패키지도 분석할 수 있습니다. SpotBugs는 주로 코드의 정확도(Correctness), 스타일(Style), 성능(Performance) 등 코드 품질에 관련된 패턴을 분석해서 알려줍니다. 또한 사용자가 원하는 패턴을 추가하여 사용할 수 있도록 플러그인 방식을 지원합니다.

보안 평가 도구로 SpotBugs를 선택한 이유는 다음과 같습니다.

  • CoveritySonarQube와 같은 다른 정적 분석 도구에 SpotBugs를 추가로 붙여서 사용할 수 있습니다. 따라서 SpotBugs에 새로운 패턴을 추가하면 다른 도구에서도 쉽게 해당 패턴을 적용하여 결과를 볼 수 있습니다.
  • 웹 애플리케이션의 보안을 검수할 수 있는 FSB(Find Security Bugs) 플러그인이 있습니다. 다양한 프레임워크를 사용한 자바 애플리케이션을 수월하게 정적 분석하기 위해서는 각 프레임워크의 구조에 맞게 처리하는 것이 중요합니다. FSB 플러그인은 SpotBugs 도구를 기반으로 각 프레임워크의 구조를 분석하여 새로운 패턴을 쉽게 추가할 수 있도록 구성되어 있습니다(최근 Spring 프레임워크의 원격 명령 실행 취약점들(CVE-2018-1260CVE-2018-1273)이 이 플러그인에 의해 발견되기도 했습니다).
  • 애플리케이션 테스트를 각종 빌드 도구(MavenGradle 등), 개발 도구(IntelliJEclipse 등)에 연동하여 쉽게 진행할 수 있습니다.

SpotBugs는 바이트코드를 분석하기 때문에 소스 코드 없이 jar 파일만으로도 분석이 가능합니다. 다음은 커맨드 라인에서 SpotBugs 도구를 이용하여 Spring 프레임워크의 spring-boot-2.1.2.RELEASE.jar 파일을 분석한 결과입니다.

> $SPOTBUGS_HOME/bin/spotbugs -pluginList ~/codes/find-sec-bugs/plugin/target/findsecbugs-plugin-1.8.0.jar -auxclasspath org.springframework spring-boot-2.1.2.RELEASE.jar
M D Eq: org.springframework.boot.context.TypeExcludeFilter.equals(Object) is unusual  At TypeExcludeFilter.java:[lines 77-78]
M V EI2: new org.springframework.boot.context.event.SpringApplicationEvent(SpringApplication, String[]) may expose internal representation by storing an externally mutable object into SpringApplicationEvent.args  At SpringApplicationEvent.java:[line 34]
M V EI: org.springframework.boot.context.event.SpringApplicationEvent.getArgs() may expose internal representation by returning SpringApplicationEvent.args  At SpringApplicationEvent.java:[line 42]
M B RV: Exceptional return value of java.io.File.mkdirs() ignored in org.springframework.boot.system.ApplicationPid.createParentFolder(File)  At ApplicationPid.java:[line 104]
H I Dm: Found reliance on default encoding in org.springframework.boot.system.ApplicationPid.write(File): new java.io.FileWriter(File)  At ApplicationPid.java:[line 96]
H I Dm: Found reliance on default encoding in org.springframework.boot.web.reactive.result.view.MustacheView.getReader(Resource): new java.io.InputStreamReader(InputStream)  At MustacheView.java:[line 114]
H B Nm: The class name org.springframework.boot.jta.atomikos.AtomikosConnectionFactoryBean shadows the simple name of the superclass com.atomikos.jms.AtomikosConnectionFactoryBean  At AtomikosConnectionFactoryBean.java:[lines 34-56]
H C IL: There is an apparent infinite recursive loop in org.springframework.boot.jta.bitronix.PoolingDataSourceBean.getParentLogger()  At PoolingDataSourceBean.java:[line 113]
M S ERRMSG: Possible information exposure through an error message  At DefaultErrorAttributes.java:[line 129]
M S SECPTI: java/io/File.<init>(Ljava/lang/String;)V reads a file whose location might be specified by user input  At ApplicationHome.java:[line 134]
M D REC: Exception is caught when Exception is not thrown in org.springframework.boot.system.ApplicationHome.getStartClass(Enumeration)  At ApplicationHome.java:[line 83]
M V EI: org.springframework.boot.context.properties.bind.Bindable.getAnnotations() may expose internal representation by returning Bindable.annotations  At Bindable.java:[line 90]
M S ERRMSG: Possible information exposure through an error message  At DefaultErrorAttributes.java:[line 185]
H S SECOBDES: Object deserialization is used in org.springframework.boot.web.embedded.undertow.FileSessionPersistence.readSession(ObjectInputStream)  At FileSessionPersistence.java:[line 117]
M B RV: Exceptional return value of java.io.File.mkdirs() ignored in org.springframework.boot.web.embedded.undertow.FileSessionPersistence.getSessionFile(String)  At FileSessionPersistence.java:[line 122]
M B RV: Exceptional return value of java.io.File.delete() ignored in org.springframework.boot.web.embedded.undertow.FileSessionPersistence.clear(String)  At FileSessionPersistence.java:[line 129]
M B Se: org.springframework.boot.logging.LoggerConfigurationComparator implements Comparator but not Serializable  At LoggerConfigurationComparator.java:[lines 29-50]
......

복잡한 애플리케이션을 분석하면 이처럼 발견되는 취약점이 많습니다. 하지만 자세히 살펴보면 이 중에 상당수는 잘못되거나 의미 없는 분석 결과라는 것을 알 수 있습니다. 그렇기 때문에 실제 개발 과정에서 이러한 정적 분석 도구를 사용하기 위해서는 외부 코드를 제외한 제한된 범위의 소스 코드 내에서 분석을 수행해야 합니다. 그러면 이제 실제로 보안 검수할 때 정적 분석 도구를 어떻게 활용하고 개선할 수 있는지 알아보겠습니다.

새로운 버그 패턴 추가하기

보안 검수를 진행하다가 새로운 버그 패턴을 발견하면 이를 정적 분석 도구에 추가하여 이후 개발 및 배포 과정에서 같은 실수가 발생하지 않도록 방지할 수 있습니다. 실제 LINE의 서비스 중 하나를 검수하면서 발견했던 문제점을 예시로 새로운 패턴을 추가하는 방법을 살펴보겠습니다.

문제점 발견 및 패턴 추가

저희가 서비스에서 발견한 문제점은 Spring Security의 @PreAuthorize 주석(annotation)에 hasIpAddress 표현을 사용하면서 발생한 문제였습니다. Spring Security 문서에서 볼 수 있듯이 hasIpAddress는 Web Security Expression에만 존재하고, @PreAuthorize는 Method Security Expression을 사용해야 하기 때문에 해당 표현(expression)은 사용할 수 없습니다. 하지만 @PreAuthorize에 잘못된 표현을 넣더라도 빌드는 실패하지 않기 때문에 개발자가 정한 주소(127.0.0.1)만 허용하도록 다음과 같은 코드를 작성하는 게 가능합니다.

PreAuthorize를 잘못 사용한 예시

@Controller
public class IndexController {
    @PreAuthorize("hasIpAddress('127.0.0.1')")
    @GetMapping("/")
    public String index() {
        [...]
    }
}

이 코드를 추가한 후 적절한 테스트를 하지 않는다면 해당 메서드는 접근 제어가 제대로 동작하지 않는 상태가 됩니다.

Detector 구현

위 패턴은 다른 조건과 상관없이 항상 잘못된 패턴이므로 정적 분석으로 쉽게 찾을 수 있습니다. SpotBugs는 Detector 클래스를 상속해서 각 버그에 맞는 detector를 구현합니다. 위 패턴을 detector로 추가하면 다음과 같습니다.

SpringInvalidPreAuthorizeDetector.java

public class SpringInvalidPreAuthorizeDetector implements Detector {
    [...]
    @Override
    public void visitClassContext(ClassContext classContext) {
        JavaClass javaClass = classContext.getJavaClass();
        method : for (Method m : javaClass.getMethods()) {
            for (AnnotationEntry ae : m.getAnnotationEntries()) {
                // PreAuthorize Annotation인 경우,
                if ("Lorg/springframework/security/access/prepost/PreAuthorize;".contains(ae.getAnnotationType())) {
                    ElementValuePair[] evPairs = ae.getElementValuePairs();
                    // PreAuthorize has one parameter
                    if (evPairs.length == 1) {
                        String value = evPairs[0].getValue().stringifyValue();
 
                        // value에 hasIpAddress를 사용한다면 bug를 report
                        if (value.matches("hasIpAddress(.+)")) {
                            bugReporter.reportBug(new BugInstance(this, SPRING_INVALID_PREAUTHORIZE, Priorities.HIGH_PRIORITY)
                                    .addClassAndMethod(javaClass, m));
                        }
                    }
                    continue method;
                }
            }
        }
    }
    [...]
}

분석 결과

Detector를 추가한 FSB 플러그인을 빌드한 후 SpotBugs를 실행해 보았습니다. 다음과 같이 버그를 잘 찾아내는 것을 확인할 수 있습니다.

> $SPOTBUGS_HOME/bin/spotbugs -pluginList ~/codes/find-sec-bugs/plugin/target/findsecbugs-plugin-1.8.0.jar -auxclasspath com.linecorp.graylab.test build/classes/
H S SECSIA: hasIpAddress는 @PreAuthorize Annotation에 사용할 수 없습니다.  At IndexController.java:[line 13]

새로운 프레임워크에 대응하기

앞서 말한 Taint 분석을 사용하여 정적 분석을 하기 위해서는 어떤 변수가 사용자의 입력인지 알아야 하고, 해당 변수가 취약한 함수를 실행할 수 있는지까지 추적해야 합니다. 하지만 복잡한 구조의 프레임워크를 사용하는 상황에서는 입력을 정확히 추적하기가 쉽지 않습니다. 예를 들어 Spring 프레임워크를 사용하는 상황에서 자바 애플리케이션이 HTTP 요청을 받아 개발자가 작성한 코드까지 도달하는 과정은 다음과 같은데요. 한눈에 보기에도 추적하는 게 쉽지 않아 보입니다.

따라서 이 과정을 모두 정적으로 분석하는 것은 어렵기 때문에 FSB 플러그인에서 자주 쓰이는, 프레임워크의 정적 분석에 도움이 되는 데이터를 미리 정의해 놓았습니다. 예시로 보여드린 Spring 프레임워크는 컨트롤러에서 @RequestParam과 같은 주석(annotation)을 사용해 HTTP 요청의 인자를 받아올 수 있습니다. 그러므로 @RequestParam을 사용한 변수는 모두 사용자의 입력으로 처리할 수 있습니다. 이렇게 주석을 사용해 처리하면 해당 메서드에 진입하기 전에 사용자의 입력을 가정하고 분석할 수 있게 됩니다. 즉, 프레임워크에서 실행되는 내용을 분석하지 않아도 되는 것입니다. 현재 FSB 플러그인에는 다양한 프레임워크에 대응할 수 있는 데이터들이 정의되어 있지만, 만약 새로운 프레임워크를 분석하고자 한다면 앞서 설명한 정보들을 추가로 업데이트해야 합니다.

이번 글에서는 LINE에서 개발하여 오픈소스로 공개한 Armeria 라이브러리를 예시로 진행해 보겠습니다. 프레임워크에 새롭게 대응하기 위해 필요한 정보는 다음과 같습니다.

  • 사용자의 입력이 들어있는 변수(taint source)
    • 예시: @RequestParam(Spring 프레임워크), javax.servlet.http.HttpServletRequestWrapper.getParameter()(자바 servlet)
  • 검증되지 않은 사용자의 입력을 받으면 문제가 되는 메서드(sink) 
    • 예시: java.lang.Runtime.exec()

이 두 가지를 어떻게 FSB 플러그인에 적용할 수 있는지 살펴보겠습니다.

예제 작성

Armeria를 이용한 애플리케이션 예제를 임의로 취약점을 넣어 만들었습니다. 정적 분석으로 취약점을 발견할 수 있도록 FSB 플러그인에 반영해보겠습니다.

SampleService.java

package com.linecorp.graylab.test.armeria;
 
import com.linecorp.armeria.client.HttpClient;
import com.linecorp.armeria.common.HttpParameters;
import com.linecorp.armeria.server.ServiceRequestContext;
import org.springframework.stereotype.Component;
import com.linecorp.armeria.server.annotation.Get;
import com.linecorp.armeria.server.annotation.Param;
 
import java.io.IOException;
 
@Component
public class SampleService {
    @Get("/rce1")
    public String rce1(HttpParameters parameters) throws IOException {
        String name = parameters.get("name");
        Runtime.getRuntime().exec(name);
        return "rce1";
    }
 
    @Get("/rce2/{name}")
    public String rce2(ServiceRequestContext ctx) throws IOException {
        String name = ctx.pathParam("name");
        Runtime.getRuntime().exec(name);
        return "rce2";
    }
 
    @Get("/rce3/{name}")
    public String rce3(@Param String name) throws IOException {
        Runtime.getRuntime().exec(name);
        return "rce3";
    }
 
    @Get("/ssrf")
    public String test(HttpParameters parameters) throws IOException {
        HttpClient httpClient = HttpClient.of("http://" + parameters.get("url"));
        httpClient.get("/internal/" + parameters.get("path")).aggregate().join();
        return "ssrf done";
    }
}

사용자 입력 추가하기

Armeria에서는 HTTP 요청에 포함된 사용자의 입력을 크게 두 가지 방식으로 받을 수 있습니다. 첫 번째는 클래스를 직접 인자로 받는 경우고, 두 번째는 Spring처럼 주석을 가진 인자를 통해 전달받는 경우입니다.

먼저 위 코드에서 rce1rce2test 메서드에서처럼 ServiceRequestContextHttpParameters와 같은 클래스를 직접 인자로 받는 경우엔, 다음과 같이 인자로 받은 클래스의 메서드 중 사용자의 입력으로 값이 만들어지는 메서드를 FSB에서 정의한 방식에 맞게 plugin/src/main/resources/taint-config/armeria.txt 파일에 추가합니다(파일을 정의하는 방법에 대한 자세한 내용은 이곳을 참고해주세요).

com/linecorp/armeria/common/HttpParameters.get(Ljava/lang/Object;)Ljava/lang/Object;:TAINTED
com/linecorp/armeria/common/HttpParameters.get(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;:TAINTED
com/linecorp/armeria/common/HttpParameters.getAndRemove(Ljava/lang/Object;)Ljava/lang/Object;:TAINTED

com/linecorp/armeria/common/HttpParameters.getAndRemove(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;:TAINTED
com/linecorp/armeria/common/HttpParameters.getAll(Ljava/lang/Object;)Ljava/util/List;TAINTED
com/linecorp/armeria/common/HttpParameters.getAllAndRemove(Ljava/lang/Object;)Ljava/util/List;TAINTED

com/linecorp/armeria/server/ServiceRequestContext.pathParam(Ljava/lang/String;)Ljava/lang/String;:TAINTED
com/linecorp/armeria/server/ServiceRequestContext.pathParams()Ljava/util/Map;TAINTED

그리고 추가된 파일을 플러그인에서 처리할 수 있도록 다음과 같이 TaintDataflowEngine 클래스를 수정합니다.

TaintDataflowEngine.java

public class TaintDataflowEngine implements IMethodAnalysisEngine<TaintDataflow> {
 
    private static final FindSecBugsGlobalConfig CONFIG = FindSecBugsGlobalConfig.getInstance();
    private static final Logger LOGGER = Logger.getLogger(TaintDataflowEngine.class.getName());
    private static final String TAINT_CONFIG_PATH = "taint-config/";
    private static final String[] TAINT_CONFIG_FILENAMES = {
        "android-taint-sql.txt",
        "collections.txt",
        [...]
        "armeria.txt"  // <--- 새로운 파일 추가
    }
    [...]
}

두 번째로 위 코드에서 rce3 메서드와 같이 주석(annotation)을 가진 인자를 통해 전달받는 경우에는 FSB 플러그인의 plugin/src/main/resources/taint-config/taint-param-annotations.txt 파일에 다음과 같이 값을 추가합니다.

com/linecorp/armeria/server/annotation/Header
com/linecorp/armeria/server/annotation/Param

여기까지 진행하면 정적 분석을 할 때 Armeria 서버에서 처리해 주는 사용자의 입력을 FSB 플러그인이 인식할 수 있습니다.

새로운 Sink 추가하기

Armeria의 클라이언트는 HTTP, ThriftgRPC 등 다양한 프로토콜을 지원하는데요. 주로 HTTP 프로토콜을 처리하는 과정에서 SSRF(Server-Side Request Forgery)가 발생합니다. 따라서 Armeria의 HTTP 클라이언트에 존재하는 sink를 추가해 보겠습니다. 실제로 보안 검수에 사용하기 위해서는 문제가 될 수 있는 모든 sink를 추가해야 하지만, 이번 글에서는 예제 애플리케이션에 존재하는 버그를 찾을 수 있는 sink만 대상으로 하겠습니다.

예제 애플리케이션을 보면 HttpClient.of()HttpClient.get() 메서드가 각각 URI와 Path를 인자로 받는 것을 알 수 있습니다. 따라서 이 두 메서드를 plugin/src/main/resources/injection-sinks/armeria.txt 파일에 추가합니다.

com/linecorp/armeria/client/HttpClient.of(Ljava/lang/String;
[Lcom/linecorp/armeria/client/ClientOptionValue;)Lcom/linecorp/armeria/client/HttpClient;:1
com/linecorp/armeria/client/HttpClient.get(Ljava/lang/String;)Lcom/linecorp/armeria/common/HttpResponse;:0

이 파일을 SSRF를 감지하는 Detector(com.h3xstream.findsecbugs.scala.SSRFDetector)에서 읽을 수 있도록 수정해 줍니다.

// 생성자에 아래 줄을 추가
loadConfiguredSinks("armeria.txt", URLCONNECTION_SSRF_FD);

이제 HttpClient.of(String, ClientOptionValue...) 메서드와 HttpClient.get(String) 메서드는 SSRF가 발생할 수 있는 sink로 분석하게 됩니다.

분석 결과

위 작업이 반영된, 새로 빌드한 FSB 플러그인을 사용하는 SpotBugs로 예제 애플리케이션을 분석해 보면 다음과 같이 버그를 잘 찾아내는 것을 확인할 수 있습니다.

> $SPOTBUGS_HOME/bin/spotbugs -pluginList ~/codes/find-sec-bugs/plugin/target/findsecbugs-plugin-1.8.0.jar -auxclasspath com.linecorp.graylab.test build/classes/
H S SECCI: This usage of java/lang/Runtime.exec(Ljava/lang/String;)Ljava/lang/Process; can be vulnerable to Command Injection  At SampleService.java:[line 19]
H S SECCI: This usage of java/lang/Runtime.exec(Ljava/lang/String;)Ljava/lang/Process; can be vulnerable to Command Injection  At SampleService.java:[line 32]
H S SECCI: This usage of java/lang/Runtime.exec(Ljava/lang/String;)Ljava/lang/Process; can be vulnerable to Command Injection  At SampleService.java:[line 26]
M S SECSSSRFUC: This web server request could be used by an attacker to expose internal services and filesystem.  At SampleService.java:[line 38]
H S SECSSSRFUC: This web server request could be used by an attacker to expose internal services and filesystem.  At SampleService.java:[line 39]

마치며

지금까지 정적 분석 도구인 SpotBugs와 Find Security Bugs 플러그인을 이용한 애플리케이션 보안 검수 방법에 대해 살펴보았습니다. SpotBugs는 다른 개발 도구는 물론 다른 분석 도구에도 쉽게 통합시킬 수 있기 때문에 정적 분석을 처음 시도할 때 시작하는 도구로 삼기 좋습니다.

이번 글에서는 아주 간단한 검수 케이스들을 다루었는데요. 실제 보안 검수 업무에 SpotBugs와 같은 도구를 도입하기 위해서는 많은 추가 작업이 필요합니다. 예를 들어 개발자가 작성한 메서드에서 사용자의 입력을 검증하는 코드가 있다면 입력값이 검증을 거치기 때문에 더 이상 taint source가 아니므로 버그로 리포팅하지 않아야 하는데요. 정적 분석 도구들은 이러한 상황을 처리할 수 있는 적절한 API를 제공하고 있습니다. 버그 패턴에 맞는 detector들을 지속적으로 추가한다면 보안 검수를 할 때 간단한 코딩 실수를 찾아내는 일이 좀 더 쉬워집니다. 따라서 보안 검수 업무 담당자는 복잡한 버그를 찾는 데 더욱 집중할 수 있게 됩니다.

LINE에서는 SpotBugs 외에도 다양한 소프트웨어 분석 제품을 테스트해 보고 실제 개발과 배포 프로세스에 적용하기 위해 노력하고 있습니다. 소프트웨어 검증 과정이 배포 과정에 포함된다면 사소한 실수로 일어나는 보안 문제들을 많이 줄일 수 있을 것입니다.

이상 글을 마치겠습니다. 긴 글 읽어주셔서 감사합니다.