Zest와 ZAP! 강력한 보안 테스트 루틴을 만들어봐요 ⚡️
What is Zest
Zest는 Mozilla 보안팀에서 만든 JSON 기반의 스크립팅 언어입니다. 보다 쉬운 웹 테스팅을 위해서 만들어졌고, 저는 테스팅 시 ZAP에서 자주 사용합니다.
Zest in ZAP
사실 JSON 포맷 자체가 rewrite가 좋은 포맷은 아니라서(그래서 config는 yaml이나 toml을 많이 쓰죠) 직접 작성하면서 쓰기에는 좀 불편한 감이 많이 있습니다. 다만 이 Zest가 ZAP 안에서 사용하는 경우 GUI Interface를 통해 로직을 통제할 수 있기 때문에 이러한 불편함은 사라지게 됩니다.
Zest Structure
Zest는 JSON 포맷으로 스크립트의 타입, 파라미터 등을 명시할 수 있습니다. 각 값의 이름은 직관적이라 한번 쓱 보시면 어떤 역할을 하시는지 알 수 있습니다.
{
"about": "Zest에 대한 설명",
"zestVersion": "0.8",
"title": "스크립트 이름",
"description": "스크립트 설명",
"prefix": "https://www.hahwul.com",
"type": "StandAlone",
"parameters": {
"tokenStart": "{{",
"tokenEnd": "}}",
"tokens": {},
"elementType": "ZestVariables"
},
"statements": [...],
"authentication": [],
"index": 0,
"enabled": true,
"elementType": "ZestScript"
}
Statements
Zest는 statements란 값을 기반으로 동작합니다. 이 값은 실제 처리할 액션이 담기는 값으로 Array 형태를 띕니다. Zest를 실행하면 이 Array를 순차적으로 실행하며 웹 요청을 발생하고 Action, Assertions, Contidion, Assignment에 따라서 로직을 처리하고 결과를 보여줍니다.
{
"statements": [
{
"url": "https://www.hahwul.com/",
"data": "HTTP Body",
"method": "GET",
"headers": "Upgrade-Insecure-Requests: 1\r\nSec-Fetch-Dest: document\r\n",
"response": {
"url": "https://www.hahwul.com/",
"headers": "HTTP/1.1 200 OK\r\nConnection: keep-alive\r\n\r\n",
"body": "Response body",
"statusCode": 200,
"responseTimeInMs": 437,
"elementType": "ZestResponse"
},
"assertions": [
{
"rootExpression": {
"code": 200,
"not": false,
"elementType": "ZestExpressionStatusCode"
},
"elementType": "ZestAssertion"
},
{
"rootExpression": {
"length": 16377,
"approx": 1,
"variableName": "response.body",
"not": false,
"elementType": "ZestExpressionLength"
},
"elementType": "ZestAssertion"
}
],
"followRedirects": false,
"timestamp": 1641533119265,
"cookies": [],
"index": 1,
"enabled": true,
"elementType": "ZestRequest"
}
]
}
코드로는 굉장히 길어보이지만, ZAP에서 보면 좌측 메뉴와 같이 단순하게 표현됩니다.
Condition (ifStatements)
일반 언어에서 분기문(if)을 생각하시면 됩니다. Condition을 설정해서 해당 Statement에서의 Response 등에 따라서 다른 Req를 발생시키거나 결과로 표현하는 등 여러가지 액션이 가능합니다.
하나 예시를 들어보면, 제가 Response code에 따라 분기하는 Condition을 추가했습니다. THEN⇒google ELSE⇒hahwul 로 추가 요청을 하도록 구성했고, 실제로 이 Zest를 실행해보면 Response code가 200이기 때문에 아래 이미지처럼 google.com으로 추가 웹 요청이 발생합니다.
간단하죠? 단순히 이런걸 이용하면 인증 실패 시 다른 계정으로 로그인 처리라던가, 특정 플로우를 순서대로 진행하면서 자동화된 테스팅 로직을 구성할 수 있죠.
Assertions
Assertion은 검증을 위한 기능입니다. 보통 개발할 때 테스트 코드에서 Assertion 처리하는 것과 동일한 개념이죠. 우리가 지정한 룰 이외의 결과가 있다면 이를 표현해주어 알 수 있도록 제공해줍니다.
예시로 제가 Regex로 Assertion을 걸었습니다. Response에 hahwul!! 이란 글자로 정규식 매칭을 시도했고, 만약 조건에 부합하지 않는다면(매칭이 안되면) 아래 이미지처럼 X 표기와 함께 결과를 나타내줍니다.
위 과정을 모두 pseudocode로 표현하면 이런 느낌이겠죠.
r = get("https://www.hahwul.com")
if r.StateCode == 200
r2 = get("https://www.google.com")
regex = /hahwul!!/g
assert(r2.match(r.Response))
else
get("https://www.hahwul.com")
end
이런 형태로 Assertion 처리를 할 수 있습니다. 개인적으로 저는 Assertions를 AAA(Authentication, Authorization and Accounting) 관점으로 테스팅해야할 때 자주 사용합니다. (대량으로 권한검사할 때 진짜 편해요)
참고로 Assertions는 Response code, Regex, Length 값으로 처리할 수 있고, 이 때 Length는 일치하는 퍼센트를 지정할 수 있어서 동적으로 변하는 Response에도 어느정도 대응할 수 있습니다.
Action
Action은 실행할 액션을 지정하는 것을 의미합니다. 위에 문법과 결합하면 특정 조건일 때 Active Scan을 한다던가, 특정 변수를 세팅한다던가 할 수 있습니다.
하나 예시로 아까 THEN에서 Print하는 Action과 Sleep 3초, 그리고 기존의 다른 스크립트를 동작하는 Action 이렇게 3개를 넣어두고 구동하면 순서대로 실행됩니다.
이러면 뭐 특정 조건에서만 원하는 스크립트를 동작시키거나, ZAP에서 스캐닝을 할 수 있겠죠?
Assignment
Assignment은 변수를 컨트롤하는 기능인데요. 이를 통해서 난수를 생성하거나 특정 값을 계산하거나, 이를 form이나 특정 attribute의 value로도 쓸 수 있습니다.
하나 예시를 들어보면, 아까 THEN 구문에서 몇개 지우고 Assign으로 난수를 생성하고, Print Action으로 해당 변수 값을 찍어보겠습니다.
그러면 순서에 따라서 처리 후 Print로 인해 생성된 값을 볼 수 있습니다. 자 그럼 이걸 실제론 어떻게 쓸까요? 제가 위에선 임의로 그냥 난수값을 넣었지만, 이 변수들은 Zest 전체적으로 사용할 수 있어서 이렇게 이후 후속 요청등에서 사용할 수 있습니다.
그래서 이걸 활용하면 CSRF Token을 자동으로 핸들링(물론 이건 AntiCSRF Token이란 기능이 있긴해요)한다거나 OAuth에서 특정 값만 사용하는 등 여러가지 방법이 있겠죠?
아 참 참고로 변수는 {{변수명}} 형태로 사용하실 수 있습니다 😎
Loop
마지막으로 Loop입니다. 이름 그대로 반복문이에요. 그래서 아래 이미지처럼 String(line 단위), File, Interger 등 여러가지 패턴으로 반복문을 만들 수 있습니다.
예를들면, 로그인 페이지에 Zest를 사용하고 미리 정해둔 ID와 PW를 String으로 처리하면 여러가지 계정을 한번의 Task로 하나하나 로그인하면서 테스트할 수 있겠죠?
Zest Record using ZAP
ZAP에선 Zest record란 기능을 통해 Proxy 요청을 모두 Zest 스크립트로 포함시킬 수 있습니다. 직접 하나하나 추가하는 것보다 훨씬 빠르게 넣을 수 있어서, 저도 종종 사용하는 기능입니다 .
예전에 관련된 글을 작성했었으니, 참고하셔서 한번 써보셔도 좋을 것 같습니다.
Conclusion
이외에도 기능이 진짜 많습니다. 저도 원래 잘 안쓰다가 작년 봄부터 하나하나 익히고 실제 업무에서 사용중인데, 익숙해지면 JS, Ruby, Python 등의 스크립팅보다 훨씬 편리하게 사용할 수 있게 됩니다.
ZAP 사용자들의 대부분이 ZAP의 가장 큰 장점을 스크립팅으로 꼽고 있습니다. 자신만의 스크립트와 방식을 잘 만들어두면, 어떤 도구도 따라할 수 없는 자동화와 테스팅 플로우를 가질 수 있게됩니다.
혹시나 ZAP 유저라면 한번 Zest의 세계로 와보시는 것도 추천드려봅니다 :D