이미 제대로 처리가 된 .ts파일들과 m3u8파일을 간단하게 뿌려주는 서버를 작성한 뒤, 엣지와 크롬 브라우저로 접속했을 때 크롬 브라우저는 재생이 되지 않고 엣지는 재생이 되었다.


여기까지 진행된 소스코드의 스냅샷 링크는 다음과 같다.


https://github.com/Einstrasse/hls-service/tree/20b3434fa3dcf62fa61e5ea31b685d871a3221ad


이때 소스코드를 다시 확인해보니, hls-server 노드 패키지의 정확한 사용법을 몰라서 직접 그 부분을 nodejs http server단에 구현을 했었다.



HLS 관련 토막글에서 대부분 브라우저에서 호환이 된다고 이야기를 들었던 것 같은데, 결국 그런 문서들은 공식 문서가 아니라서 정확도가 떨어질 수 밖에 없다고 생각하고 직접 확인해보도록 해보았다.


http://html5test.com/

위 링크에서 해당 브라우저의 html5를 얼마나 지원하는지에 대한 정보를 확인할 수 있다.

크롬 브라우저로 확인해보니, HTTP Live Streaming/HLS 지원 여부에 No라고 나왔다. 지원한다는 것은 새빨간 거짓말이었던 것이다. 역시 정확한 정보를 얻기 위해서는 공식 문서를 확인하거나 직접 확인하는 것이 빠르고 정확한 것 같다.

반면 엣지 브라우저로 접속을 하니, HLS를 지원하는 것으로 나타났다. 엣지 브라우저에서만 동작을 해서 내가 혹시 서버 설정을 잘못했는가에 대한 의문도 품어보았었지만, 일단은 아닌것으로 확인되었다.


크롬이 HLS 클라이언트를 브라우저 네이티브로 지원하지는 않지만, 아마 자바스크립트로 라이브러리가 있을 것으로 생각해서 hls client로 구글링을 해 보았다.


바로 뭔가 유명해보이는 깃허브 페이지가 나타났다.


https://github.com/video-dev/hls.js/




커밋 수도 많고, Star 달린 수도 많고 실제로 이 hls.js를 사용하는 서비스도 종류도 많고 유명한 곳도 많았다.

클라이언트 사이드에 이 hls.js를 적용하니 크롬에서도 음악 스트리밍 재생이 잘 되었다. 크롬 개발자 도구의 Network 탭을 보니, 이미 받은 ts파일을 다 재생할 때 쯤 새로운 요청을 보내는 식으로 아주 잘 동작하였다.


이 부분까지 진행한 git 커밋 스냅샷이다.


https://github.com/Einstrasse/hls-service/tree/c7861d0594e86273111f02541e0beccf7cf6d0e0


여기까지 진행된 바로는, 서버에 있는 이미 처리된 정적인 ts파일들과 m3u8파일로 스트리밍 재생이 가능하다는 것을 보였다.

이제 여기에 실시간으로 음성을 제공하는 방송자 입장에서 서버에 마이크로 녹음된 음성 정보들을 보내는 동작을 추가해야 한다.


웹 브라우저 마이크 API에 대해 구글을 해 보니 다음과 같은 글을 확인할 수 있었다.

https://www.html5rocks.com/ko/tutorials/getusermedia/intro/

https://developers.google.com/web/fundamentals/media/recording-audio/?hl=ko


두번째 글에서 도움을 많이 얻었다. 하지만 두번째 글에 있는 코드를 그대로 복사해서 적용할 때에는, 글에 나온대로 실행이 되지 않았다.


따라서 MDN에 있는 MediaRecorder API를 찾아보았다.


https://developer.mozilla.org/ko/docs/Web/API/MediaRecorder


확인해보니 MediaRecorder.start() API에서 인자로 timeslice를 넘겨주면, timeslice마다 ondataavailable 이벤트를 호출하게 된다. timeslice를 넣어주지 않으면 명시적으로 MediaRecorder를 stop해주지 않는 이상 ondataavailable 이벤트가 발생하지 않기 때문에 원하는 대로 실행되지 않았던 것이다.

https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/start



여기까지는 클라이언트에서 마이크 API 를 통해 녹음한 음성을 가지고 있는 부분까지 구현되었다. 아직 서버에 음성 데이터를 보내거나 받거나 하는 부분은 구현이 되지 않았다.

여기까지 구현된 모습이다.

https://github.com/Einstrasse/hls-service/tree/394c7f3d33d687c91367c58f464e6f799292e40d



HLS를 이용한 라이브 라디오 방송 웹 앱 개발기 (3)

본 포스팅은 개인적으로 진행한 토이프로젝트를 진행하면서 문제를 해결한 과정등을 정리한 포스팅입니다.


웹 브라우저에서 동작하는 네이버 뮤직은 어떻게 플러그인 하나 없이 스트리밍으로 노래를 들을 수 있을까? 라는 단순한 의문으로 간단하게 조사를 해 본적이 있는데, 네이버 뮤직 PC버전은 Http Live Streaming이라는 프로토콜을 이용해서 음원 스트리밍을 한다는 것을 알게 되었다.


HLS는 기존의 HTTP 웹서버로 쉽게 구성할 수 있도록 되어있어서 웹 서버 개발 경험이 있으면 쉽게 구현할 수 있을 것으로 보였습니다. 또한 스푼이라고 하는 라디오 방송 서비스에서도 HLS를 이용해서 라이브 음성 스트리밍으로 방송을 하도록 구현되어 있어서, 한번 비슷하게 만들어보기로 결정했다.


만들려고 하는 앱의 기능적 요구사항들은 다음과 같습니다.


1. 방송을 하는 사람은 웹 브라우저로 해당 방송을 하는 페이지로 접속합니다. 그러면 브라우저에 있는 마이크 API를 통해서 마이크에 녹음된 음성 정보를 일 정주기마다 서버로 보냅니다.

2. 서버는 받은 음성 정보를 처리해서 HLS로 전송가능한 형태로 바꿉니다.

3. 방송을 청취하는 사람은 웹 브라우저로 해당 방송을 청취하는 페이지로 접속합니다. 그러면 HLS를 통해서 방송하는 사람의 음성을 실시간으로 듣습니다.


HLS(Http Live Streaming) 프로토콜에 대한 내용은 네이버 d2에 잘 정리된 글이 있어서 해당 글을 보며 공부했다.

http://d2.naver.com/helloworld/7122


개략적인 HLS의 동작 방식을 확인한 후 비슷한 프로젝트가 있는지 한번 찾아보았더니 유사한 프로젝트가 있었다.


https://github.com/mjrusso/livestreaming-js


다만 조금 걱정이 되는 것은, 사용하는 nodejs 버전이 v0.2대 버전이고 최근 커밋이 2011년도이므로 현재 2018년인 상황에서 코드가 제대로 동작할지가 의문이었습니다. git clone을 받아서 실제로 필요한 파일들을 설치해서 구동을 시도했는데, 아니나 다를까 많은 에러 투성이었습니다.


nodejs 버전업이 많이 되면서 해당 프로젝트에서 사용한 API가 이미 deprecated되서 사라진 경우도 있었고, 결정적으로 ffmpeg의 SDK를 이용해서 미디어 데이터를 처리하는 C소스코드가 포함되어 있는데, 이 C API가 맞지 않아서 컴파일이 안되었다. 2011년도 버전 ffmpeg SDK를 사용하려고 했는데, 홈페이지에서도 구할 수 없을 만큼 오래된 SDK이라서 해당 프로젝트의 아키텍쳐와 문서를 참고해서 직접 새로 만들기로 했다.

또한 실제로 빌드가 된다고 하더라도, 해당 프로젝트는 파일 업로드를 직접 해서 스트리밍을 시키는 방식이고, 내가 원하는 프로젝트는 브라우저 마이크 API로 실시간으로 녹음되는 음성을 스트리밍 하는 것으로 조금 차이가 있다.


해당 서버를 단계별로 개발해보기로 했다.


일단 네이버 뮤직에서 m3u8파일과 ts파일들을 다운로드받아서 추출한 뒤, nodejs로 만든 간단한 서버로 뿌려주는 식으로만 작성해보았다.

Nodejs 패키지중에 hls-server라는 패키지가 있어서 확인해보았다.

https://www.npmjs.com/package/hls-server


문서가 많이 친절하지는 않지만, 대략적인 사용방법 예제코드들이 있다.

HLS 서버는 http 서버의 미들웨어 형태로 삽입되는 형식으로, http 요청 처리하는 로직 이전에 먼저 프로세싱을 하게 된다.


 

var server = http.createServer(function(req, res) { //HTTP 요청을 처리함. }); var hls = new HLSServer(server, { provider: { exists: function (req, callback) { // hls 미들웨어가 삽입된 서버에서 요청이 올 때마다 호출되는 함수 callback(null, true) // 파일이 존재하고 스트리밍을 할 경우 호출하는 콜백 callback(new Error("Server Error!")) // 500 error시 호출하는 콜백 callback(null, false) // 404 error시 호출하는 콜백 }, getManifestStream: function (req, callback) { // 적절한 .m3u8 파일을 리턴한다. // "req" is the http request // "callback" must be called with error-first arguments callback(null, myNodeStream) // or callback(new Error("Server error!"), null) }, getSegmentStream: function (req, callback) { // 적절한 .ts 파일을 리턴한다. callback(null, myNodeStream) } } }) server.listen(PORT);


npm 홈페이지에 있는 Using In-Memory Stream이란 탭에 있는 예제코드이다. 추가적으로 http 서버를 만드는 코드도 추가되었다.


HLSServer 함수의 첫번째 인자로 준 server에 해당 미들웨어가 삽입된다.

provider 객체 내의 exists함수는 해당 서버로 오는 모든 요청에 대해서 호출이 되며, Error을 던지게 되면 그 다음으로 진행하지 않고 에러를 응답하게 된다.

exists함수에서 callback(null, true)가 호출되게 되면, 요청 url의 path중 확장자를 추출해서 판단하게 되는데, 확장자가 .m3u8이면 getManifestStream 함수를 호출해서 요청을 처리하게 되고, 확장자가 .ts 이면 getSegmentStream 함수를 호출해서 요청을 처리하게 된다.

그리고 해당 확장자 (.m3u8이나 .ts)과 일치하지 않는 요청이면, httpServer 자체의 요청 처리 로직으로 넘어가서 처리되게 된다.


hls 패키지 미들웨어가 동작하는 방식이 잘 이해가 가지 않아서 소스코드를 확인해보았었다.


github에 공개된 hls-server node 패키지 소스코드 url이다.

https://github.com/RationalCoding/hls-server/blob/master/src/index.js


 

HLSServer.prototype._middleware = function (req, res, next) {
  var self = this

  var uri = url.parse(req.url).pathname
  var relativePath = path.relative(self.path, uri)
  var filePath = path.join(self.dir, relativePath)
  var extension = path.extname(filePath)

  req.filePath = filePath

  // Gzip support
  var ae = req.headers['accept-encoding'] || ''
  req.acceptsCompression = ae.match(/\bgzip\b/)

  if (uri === '/player.html' && self.debugPlayer) {
    self._writeDebugPlayer(res, next)
    return
  }

  self.provider.exists(req, function (err, exists) {
    if (err) {
      res.statusCode = 500
      res.end()
    } else if (!exists) {
      res.statusCode = 404
      res.end()
    } else {
      switch (extension) {
        case '.m3u8':
          self._writeManifest(req, res, next)
          break
        case '.ts':
          self._writeSegment(req, res, next)
          break
        default:
          next()
          break
      }
    }
  })
}


self.provider.exists 부분에 보면, 파일 확장자(extension)을 기준으로 알맞는 함수를 호출하고, 해당 확장자에 해당하지 않으면 해당 서버의 원래 처리 로직으로 가도록 되어있다. 그리고 두 확장자에 둘다 해당되지 않으면, next()를 호출해서 미들웨어에서 제어권을 넘겨준다. 이러한 함수들을 이용해서 이미 세그먼팅 된 .ts파일들과 .m3u8 매니페스트 파일로 브라우저단에서 잘 재생이 되는지 확인해보았다.


주로 쓰는 크롬 브라우저와 윈도우10에 디폴트로 설치되어있는 엣지 브라우저를 이용해서 테스트를 해 보았는데, 엣지 브라우저에서는 음악이 잘 재생되었는데, 크롬 브라우저에서는 재생이 되지 않았다. 어떻게 된 것인지 확인해보기 위해 HLS와 관련된 문서들을 찾아보았다.

본 포스팅은 구글 프로젝트 제로의 멤버인 Ivan Fratric이 'So you want to work in security?'라는 제목으로 정보보안 직종에 종사하고 싶은 후배들에게 한 블로그 포스팅을 번역한 포스팅입니다.


번역 상에 오역이 있을 수 있으므로, 원문도 같이 첨부합니다.


원문 링크 : http://ifsec.blogspot.kr/2018/02/so-you-want-to-work-in-security-and-for.html?m=1


트위터를 써라



갑자기 트위터라는 SNS하라는 것이 이상한 소리처럼 들리겠지만, 많은 보안 커뮤니티가 정보를 공유하기 위해 트위터를 쓴다. 그리고 최근 연구, 취약점, PoC, 컨퍼런스 발표자료 소스같은것도 올라온다. 나는 이런것들이 어떻게 올라오는지 모르지만, 불필요하게 긴 토론이 있는 것 보다는 이런식으로 공부 자료들에 대한 링크를 공유하는 것이 더 높아 보인다. 그러므로 트위터에서 관심있는 보안 관련 자료들을 게시하는 사람들을 찾아라.



트위터 말고도, 너가 관심있을 만한 자료들을 찾을 수 있는 곳이 또 있다. r/netset고 해커뉴스이다.(비록 요녀석들은 보안과는 관계없는것들도 공유하긴하지만) 컨퍼런스 발표자료와 녹음본들도 확인해라(그런 자료들이 많다, 하지만 모두 다 좋은 자료는 아니긴 하다. 기술적인 부분에만 집중해서 봐라)


CTF를 하는 것은 배우기 좋은 방법 중 하나이다.



나는 해본적이 없지만 남에게는 추천하는 이상한 충고가 또 있지만, 내가 학습 곡선을 그린 것을 기억하는가? CTF는 다양한 어려움에 직면하기 때문에 학습을 점근적으로 하도록 해줄 수 있다.(각 문제의 점수로 난이도를 알 수 있다.) 따라서 쉬운 것 부터 시작해서 공부하게 할 수 있다. 예를 들어 미티게이션이 해제된 체 있는 익스플로잇 문제가 있는 경우가 있다. 그런 것을 익스플로잇 할 수 있는 버그와 방법이 있다는 것을 알게 되는식으로 공부할 수 있다.



CTF는 거의 매주 있으며 대부분 온라인에서 참여할 수 있다. 여기서 일정을 찾을 수 있다.(ctftime.org) 문제를 푸는 것을 실패한 경우, 문제 푼 사람들의 write-up을 확인하는 것을 잊지 말라.



CTF는 만족스러운 경험일 수 있지만, 그 이후에 실제 목표를 달성하기 위해 리얼월드 취약점을 찾으러 나가고 시도하는 것을 두려워 하지 말라. 너는 스스로 놀랄것이다.


그리고 리얼월드 취약점을 찾으러 나갔을때


실패를 두려워하지 말라, 많이하더라도


특히 최근에 취약점 분석 연구는 너를 매우 시무룩하게 할 수 도 있다. 너가 시도하는 대부분의 타겟들은 너가 원하는 대로 동작하지 않을 것이고, 너는 그걸 받아들여야 한다. 그렇다고 시도를 포기하지 마라. 이런 일은 너에게만 일어나는 것이 아니라, 나한테도 일어나고 다른 경험많은 연구원들도 똑같이 겪는 일이다. 하지만 다른사람들은 결국 성공하기 때문에 이런 일은 너에게만 일어난다고 생각하기 쉽다. 중요한 점은, 너의 아이디어가 실패했을 경우 왜 실패했는지를 파악하는 것이다.


너는 너 생각보다 똑똑하다(반대로, 다른 사람들은 너가 생각하는 것 보다 똑똑하지 않다)



다른 사람들은 "너는 개발자 보다 똑똑하지 않다"라는 조언을 주었기 때문에, 이 조언은 논란의 여지가 있을 수 있습니다. 이러한 조언은 맞는 말이고, 실제로 이미 산업에 있는 많은 사람들에게 좋은 조언입니다. 하지만, 이제 막 입문하였거나 입문하려는 사람들에게는 잘못된 조언일 수 있습니다. 해당 분야에서 해낸 것이 아무것도 없는 상태에서 똑독한 사람들이 직접 하는 일들을 보면, 자신의 능력을 의심하기 쉽다는 것입니다. 내 개인적인 예를 하나 들어보겠습니다.



지금은 이상하게 들릴 수 있지만, 내가 보안을 취미로 처음 시작했을 때 Windows에서 버그를 발견할 만큼 충분히 "l33t"가 될 수 없을 것이라 생각했습니다. 그리고 내가 우연히 윈도우 버그를 발견하기 전 까지 시도조차 하지 않았습니다. 나는 오래된 이미지 라이브러리를 퍼징하고 있었고 잠시후 크래시를 일으키는 샘플이 있었습니다. 그리고 내가 우연히 그 크래시 샘플을 윈도우에서 눌렀을때, 윈도우 익스플로러가 크래시가 났고 그것은 CVE-2008-3013이었습니다.



다른 케이스도 있다. 소프트웨어를 리뷰하는 도중에, 이러한 생각이 들 수가 있다. "와, 바보같이 개발자들은 분명 그렇게 생각했다." 이러한 일은 종종 일어날 수 있다. 이거는 그들이 멍청해서그런게 아니라 그 당시 다른 문제를 생각했기 때문입니다. 하지만 "나는 그들보다 더 똑똑하다"는 사고 방식을 통해 자신이 스스로 설정한 한계를 뛰어넘을 수는 있지만, 다른데서 겸손해지게 되는 문제를 일으킬 수 있습니다.


당신이 다른사람들, 특히 개발자들과 이야기할 때 그런 생각을 놓을 때입니다. 당신이 그들을 적으로 만나지 않고 같이 일할 사람으로 만난다면 대화하면서 즐거운 시간을 보낼 수 있을 것입니다. 이것은 당신이 듣는 모든것을 믿으라는 뜻은 아닙니다. 명심하세요, 그들은 그들이 작성한 코드의 전문가이지만 당신은 보안에서 전문가입니다.


내가 가지고 있는 기술을 세상에 보여줄 준비가 되었을때 무엇을 해야하나요?



처음에는 돈을 벌면서 무언가를 할 수 있습니다. 많은 회사들은 자신들의 제품에 있는 버그를 찾기 위해 크고 작은 버그바운티를 합니다. 구글과 페이스북, 마이크로소프트가 그렇지요.



버그바운티 상금이 없는 제품을 보더라도, 많은 사람들이 사용하는 것이므로 버그를 발견하면 당신의 기술을 뽐낼 수 있는 좋은 방법이 될 수 있습니다. 그러면 다른 사람들도 당신을 알아채기 시작할 것입니다.



불균형적으로 많은 관심을 얻겠지만, 취약점을 리포트하는 것은 커뮤니티에 기여하는 유일한 방법은 아닙니다. 유용한 도구를 만들고 방어 리서치를 하는 것도 멋진 방법입니다!


내가 더 알아야 하는게 있나요?


보안 연구원의 삶은 당신이 상상하는 것 처럼 영광스럽지 않을 수 있습니다. 컴퓨터 앞에 오랫동안 앉을 것입니다. 그래서 뭔가 괜찮은 아이디어를 얻었는데 그것이 커리어에 좋은 경로가 아닐 수 있다. 또한 꽤 지적으로 도전적이고 일상적이지 않은 일입니다. 이것은 꽤나 보람이 있을 수 있지만 정신적으로 힘들 수 있다는 것을 의미합니다.

+ Recent posts