Cryptoguard: High precision detection of cryptographic vulnerabilities in massive-sized Java projects

정보

Keywords

  • Cryptographic API misuses
  • 오탐/미탐
  • 정적 분석
  • Java

배경

  • 암호화 API 오용은 소프트웨어 보안을 위혐함
  • 또한 자동으로 대규모의 프로그램들에서 암호화 API 오용을 탐지하고 있음
  • 하지만 분석 성능과 타협하지 않으면서 오탐을 줄이는 것은 어려움

목표

  • 암호화 및 SSL/TLS API 오용을 탐지하는 분석 알고리즘들을 설계하고 구현함
  • 다양한 Apache 프로젝트 및 안드로이드 어플리케이션에서 탐지 성능을 확인해 봄
  • 테스트 해 볼 수 있는 성능확인용 테스트 케이스를 만듦

방법론

  • CryptoGuard: 언어별로 상관 없는 요소들을 탐지하는 알고리즘들을 활용하는 도구
    1. 정적 def-use 분석과 순방향 및 역방향으로 program slicings를 적용함
    2. 각 program slice에서 취약점을 탐지함
      • 취약점마다 slicing 방법이 다름(Table 1)

결과

  • 46개의 중요한 Apache 프로젝트와 6181개의 안드로이드 앱에서 많은 보안 insight를 생성함
  • 1295개의 Apache 경고를 수동으로 분석하여 1277개(98.61%)가 정탐임을 확인함

Exposing Library API Misuses via Mutation Analysis

정보

Keywords

  • Library API 오용
  • Mutation 분석

배경

  • Library API 오용은 소프트웨어에 crash 및 취약점을 야기할 수 있음
  • 다양한 정적 분석 도구들이 Library API 오용을 탐지하는 것을 제안해 왔지만, 실제로 이를 적용하기에는 무리가 있음

목표

  • 높은 정확도로 API 오용을 탐지할 수 있는 도구를 개발하고자 함
    • API 오용 패턴을 발견할 수 있는가?
    • MuBench에서 API 오용을 탐지할 수 있는가?
    • 기존 mutation 기법과 비교해서 얼마나 더 효과적이고 효율적인지?

방법론

  • API 오용 패턴에 대한 발견이 있음
    1. API 오용은 올바른 용법의 mutant로 표현할 수 있음
    2. mutant가 오용인지 아닌지는 실행하고 그 결과를 분석해 봄으로써 검증할 수 있음
      • 각 mutant에 대하여 client 프로젝트의 테스트 케이스로 실행시켜 보고 killing relations(테스트 실패한 mutant 및 test case)를 수집
  • 위의 발견을 기반으로 MutAPI 도구를 제안함

결과

  • 16개의 프로젝트에서 73개의 유명 JAVA API를 대상으로 0.78의 오용 패턴을 발견할 수 있었음 (상위 50개의 mutant 후보에 대하여)
  • MuBench에서 53개의 진짜 오용 중 26개를 탐지함

결론

  • 많은 수의 API 사용 예제를 필요로 하지 않음
  • "빈번한 패턴에서 벗어난 것은 오용이다"라는 가정에서 더 나아감

*궁금점

  • test suite에 상당히 의존하는 구조인데, 1) 이는 신뢰할 수 있는 것이며, 2) test suite가 이를 못잡는 경우는 어떻게 해결했는지?
평소 PS를 할 때, 자료구조는 크게 신경 쓰지 않았던 것 같다.

그러다가 이번 Dijkstra 문제를 풀면서 자료구조에 따라 시간초과가 발생할 수 있음을 직접 경험해 보며, 앞으로의 PS에는 자료구조도 한번 더 고민해 보기를 바라며 기록을 남긴다.

 

문제 파악

문제가 된 코드는 Dijkstra 함수 구현 부분이다.

물론 제대로 구현된 코드는 아님을 알고 있다.

순회를 할 때 queue를 만든 자료구조를 제외하고는 똑같은 코드이다.

ArrayList

fun dijkstra(N: Int, X: Int, vertex: Array<ArrayList<Int>>): Array<Int> {
    val INF = 1_000_001

    val dist    = Array(N, { INF })
    val visited = Array(N, { true })
    val queue   = arrayListOf<Int>()

    var s = X
    dist[s] = 0
    queue.add(s)

    do {
        s = queue.removeFirst()
        visited[s] = false

        vertex[s].forEach {
            if (visited[it]) {
                queue.add(it)
                dist[it] = min(dist[it], dist[s] + 1)
            }
        }
    } while (queue.isNotEmpty())

    return dist
}

LinkedList

fun dijkstra(N: Int, X: Int, vertex: Array<ArrayList<Int>>): Array<Int> {
    val INF = 1_000_001

    val dist    = Array(N, { INF })
    val visited = Array(N, { true })
    val queue   = LinkedList<Int>()

    var s = X
    dist[s] = 0
    queue.add(s)

    do {
        s = queue.removeFirst()
        visited[s] = false

        vertex[s].forEach {
            if (visited[it]) {
                queue.add(it)
                dist[it] = min(dist[it], dist[s] + 1)
            }
        }
    } while (queue.isNotEmpty())

    return dist
}

두 코드 모두에서 queue를 사용하지만 각각 ArrayListLinkedList로 차이를 보이며, queue의 맨 처음 것을 제거하고 맨 마지막에 추가하는 동일한 방식의 코드이다.

문제점으로는 ArrayList 로 queue를 구현하였을 때는 시간초과가 발생하였지만, LinkedList 로 queue를 구현했을 때는 시간초과가 발생하지 않았다.

 

원인 분석

두 자료구조(ArrayList, LinkedList)는 Java의 List interface를 구현한 Collections의 일종인데, 내부적인 동작 방식을 보면 차이를 보인다.

ArrayList의 경우 데이터의 변화(추가/삭제)가 발생하면 임시 배열을 생성하는 반면 LinkedList는 노드 간의 연결을 변화시킨다.

 

ArrayList

https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html


ArrayList의 구조를 보면 간단하게 배열을 이용하여 구현된 것을 알 수 있다.

하지만 이러한 방식은 검색에서는 효과적일 수는 있지만, 자료의 추가/삭제에는 효율적이지 못하다.

 


데이터의 추가 및 삭제가 발생하면 기존 배열로부터 임시 배열을 만들어 작업을 수행하기에 최악의 경우에는 O(N)의 시간이 걸린다. 즉, 데이터의 밀고 당기는 작업이 필요하다. 추가로 배열은 동적으로 늘어나는 것이 아니라, 배열이 꽉 찼을 경우 더 큰 배열을 만들어 옮겨야 하기에 loss가 크다.

하지만 검색 차원에서는 배열의 시작 주소에 index만을 더해 data가 존재하는 곳의 주소를 바로 알 수 있기 때문에 O(1)의 시간이 걸린다.

 

LinkedList

https://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html


LinkedList의 구조를 보면 data를 저장하는 노드간의 연결을 통해 리스트로 구현된 것을 알 수 있다.

하지만 이러한 방식은 자료의 추가/삭제에서는 효과적일 수는 있지만, 검색에는 효율적이지 못하다.

 


데이터의 추가 및 삭제가 발생하면 새로운 노드를 만들거나 삭제하고 next 노드를 갱신하면 된다. 하지만 검색을 하기 위해서는 sequential 하게 접근해야 하기에 최악의 경우에는 O(N)의 시간이 걸린다.

기존 배열로부터 임시 배열을 만들어 작업을 수행하기에 최악의 경우에는 O(N)의 시간이 걸린다. 즉, 데이터의 밀고 당기는 작업이 추가로 필요하다.

 

결론 도출

작업 종류 비교
추가/삭제 ArrayList < LinkedList
검색 ArrayList > LinkedList

List를 구성해야 하는 작업이 많다면 LinkedList 를,

구성된 List에서 검색 작업이 많다면 ArrayList를,

사용하는 것이 좋아 보인다.

'0x30 Algorithm' 카테고리의 다른 글

2진수에서 1 누적합 구하기  (0) 2023.07.04
Testcase 모음집  (0) 2021.11.20

+ Recent posts