링크 프리뷰 모듈(이라 쓰고 사실은 미디어 임베드 모듈이라 이해해야 하는 모듈)에서 몇몇 컨텐츠들을 임베드하고 있는데요.

그 중에서 깃허브카카오맵을 자체 페이지를 통해 (콘텐츠를 렌더링한 후 이를) iframe으로 불러오는 방법을 취해봤습니다.

 

그런데 이 경우에는 '시스템 설정 > 보안 설정 > 외부 멀티미디어 허용'에 자기 사이트의 호스트도 추가를 해줘야 하더군요.

잘 이해는 못하고 있지만 자기 사이트의 페이지라도 iframe으로 불러오면 보안 문제가 있다고 판단해서인 것 같은데요.

 

혹시 더 조심해야 하는 부분이 있을지 궁금합니다.

어쨌든 iframe 구현을 하는 데 성공은 한 터라 이걸 그대로 공개를 할지, 아니면 일반적인 방식으로 스크립트로 본문에 바로 렌더링을 하는 방식으로 할지 판단이 잘 안 서서요.

  • Lv19
    dispMemberLogout같이 GET으로 동작 가능한 페이지를 임베드해버리면 클릭하면 로그아웃이 되어서 아무도 댓글을 못다는 글을 만들수 있을겁니다.

    iframe으로 불러오기보다는 자체 페이지로 렌더링한 결과물을 직접 출력하는 방식이 더 좋을겁니다.
  • Lv19 Lv19
    현재는 preview 모듈의 전용 액션만 iframe용 템플릿을 열 수 있게 했는데 이 경우도 로그아웃->댓글 방해 같은 문제에 취약해지는 건가요?
    보안 쪽은 진짜 문외한이어서 그 원리를 잘 이해를 못하겠어요ㅜ
  • Lv19 Lv19

    외부 멀티미디어 허용에 자기 사이트의 호스트도 추가하면 일반 사용자가 사이트의 아무 페이지나 인클루드할수 있게 됩니다.

    ex.png
    이렇게요.(사진은 로그아웃 주소를 프레임에 띄웠습니다)

  • Lv19 Lv19
    보안 설정에 포함시킨 것 때문에 문제가 생길 수 있는 거군요.
    로그아웃 액션을 넣으면 페이지를 열어보는 사람들을 로그아웃 시키는 거구요.
    아, 이거 성가시네요;;;
  • Lv19 Lv19

    지금은 각종 proc 액션이 왠만하면 POST로만 동작 가능하게 제한이 걸려서 망정이지 안그랬으면 글 삭제나 댓글 삭제도 가능했을겁니다. 열람자에게 권한이 있는 글 한정이지만 열람자가 관리자라면 그런 제한도 없어지죠...

  • Lv19 Lv19

    그렇겠네요.
    근데 그렇다면 아이프레임으로 불러오는 페이지를 라이믹스 템플릿이 아니라 별도의 폴더에 담긴 외부페이지로 한다면,
    예컨대 xetown.com/modules/preview/libs/iframe.php?service=kakao_map&type=default&.... 같은 식으로 하고,
    보안 설정에서도 호스트 전체가 아니라 xetown.com/modules/preview/libs/ 로 한다면 어떤가요?
    그러면 왠지 지적해주셨던 문제는 회피할 수도 있을 것 같아서요.

    ... 참고로 가급적 iframe 처리를 하려는 게, 이게 게시물 본문에 직접 렌더링하는 방식보다 (에디터 단계에서 이미 완성된 콘텐츠를 아이프레임으로 딱 보여주는) 프로세스가 직관적이어서 자꾸 미련이 남아서 그럽니다ㅜ

  • Lv19 Lv19

    https://xetown.com/modules/preview/libs/../../../hall/dispMemberInfo

    ../로 위로 가버리면 그만이지요.

  • Lv19 Lv19
    헐, 대박. 그냥 렌더링으로 가야겠네요ㅠㅠ
  • Lv19 Lv7
    헉.. 이건 일종의 보안취약점 같은데요;;
  • Lv19 Lv7
    저라면 아이프레임 형태를 사용할 것 같습니다.

    다만 바로 아이프레임으로 집어넣기보단, 백단에서 넣어주는건 div고 아이프레임은 프론트단에서 동적으로 넣어주는 형태를 가져갈 것 같아요.

    <div class="link-preview" data-link="https://naver.com/"></div>

    과 같은 값을 백단에서 넣어주면, 프론트단에서 JS를 통해 .link-preview들을 찾아 그 안에 아이프레임을 넣어주는거죠.

    MutationObserver를 활용해 동적으로 생성되는 요소들도 깔끔하게 처리할 수 있어요.
    에디터 내에서의 프리뷰 처리도 가능하겠죠. (특히 외부미디어 보안 정책으로 아이프레임은 자동으로 삭제될테니 더더욱 깔끔할거구요)

    IntersectionObserver를 쓰면 스크롤 상태에 따라 레이지 로딩 처리도 가능합니다.

    물론 아이프레임 말고 ajax등으로 처리할 수도 있겠지만, 아이프레임이 스타일 충돌같은 문제에 좀 더 자유롭겠네요.
  • Lv7 Lv19
    엇, 다시 구미가 당기는데요오
    근데 data-link에 위의 YJSoft님 지적처럼 누군가 공격성 url을 작성하면 iframe을 동적으로 생성하더라도 결국 같은 결과가 되는 거 아닌가요?
    아, 정규표현식으로 data-link값을 매칭하면 문제가 없을 것도 같네요. (헷갈려요ㅋㅋㅋ)
  • Lv19 Lv7
    data-link 는 실제로 삽입될 아이프레임이 아닌 프리뷰를 출력할 링크의 값을 넣어놓고,
    /modules/preview/libs/iframe.php?link=url-from-data-link 을 아이프레임으로 삽입해주는거죠.
    이후 iframe.php 파일에서 해당 링크를 파싱하든, 페칭하든 이것저것 필요한 것들 다 처리해서, 링크의 플랫폼에 맞는 프리뷰 화면을 띄워주면 되겠죠?
  • Lv7 Lv19
    원래 안되는것을 억지로 허용한 것이니 취약점은 아닙니다. 문 좌물쇠가 아무리 튼튼하다 한들 문 옆에 열쇠를 두면 아무 의미 없죠..
  • Lv7 Lv19
    그렇겠네요. 굳이 소스에서 /modules/preview/libs/iframe.php?link=를 노출시킬 필요는 없으니..
    그러면 보안 설정에 자기 사이트 호스트를 넣지 않아도 되고 확실히 앞에서 지적됐던 보안 문제는 회피 가능할 것 같습니다. 조언 감사합니다!
  • Lv37

    서버 부하 측면에서는 절대 비추입니다.

    한 페이지에 iframe이 5개 있다면, 그 페이지 하나를 보기 위해 서버에 6번 요청하고, PHP를 6번 실행하고, DB에 6번 접속해서, 6개의 동적인 페이지를 로딩해야 합니다. 글읽기 페이지를 생성하는 요청 1번, 그리고 iframe마다 각각 1번씩.

    반면, 5개의 링크 프리뷰에 넣을 내용을 미리 본문에 HTML이나 JS 코드로 집어넣어 두었다면, 최초 페이지 로딩은 조금 느려질 수 있겠지만, 1번 로딩하는 것으로 끝날 테니 훨씬 이득입니다.

    1초에 10번 이상 PHP를 호출하면 디도스 공격으로 간주하고 IP를 차단하는 서버도 많습니다. XE타운도 뒤로가기를 너무 빨리 연타하면 429 에러가 뜨곤 하죠. 이런 글 한두 개 읽으면 바로 차단되겠네요. ㄷㄷㄷ

  • Lv37 Lv7

    전 오히려 브라우저단 캐싱과 레이지로딩을 처리할 수 있다는 점에서 서버 부하 측면에 유리하다고 생각합니다.
    CF 같은 앞단 프록시까지 사용하면 아주 오랜기간 오리진 서버에 전혀 방문하지 않고도 운용할 수 있을거구요.

    게시글 작성 시점에 MutObserver를 통해 에디터 내 프리뷰를 생성하는 기능까지 포함되면
    PHP가 처리할 것들 대부분 작성 시점에 모두 끝나고, 게시글 조회 시점엔 아주 쌩쌩하지 않을까요?

    반대로 DOM 객체 하나를 통으로 만들어내는 아이프레임 때문에 클라이언트측 성능 문제가 있지 않을까~ 생각했어요.

  • Lv7 Lv37

    동적인 페이지를 매번 생성하지 않고, 앞단 프록시를 활용할 수 있다면 괜찮을 수도 있겠지요. 그러나 불특정 다수에게 배포하는 인기 자료에서 클플을 사용한다고 가정하기는 어려우니, 대안이 있어야겠지요.

    캐싱이 가능한 구조라면 (즉 프리뷰 내용이 매번 바뀌는 것이 아니라면) 글읽기 페이지를 생성할 때 해당 데이터를 한꺼번에 다 넣어주지 못할 이유도 없지요. 아예 html 파일로 캐시를 저장해 놓고 그 파일을 불러올 수도 있고요.

    그리고 글 작성 도중 에디터에서 표시하는 것과 글읽기 화면에서 표시하는 것을 분리해서 생각하면 좋겠습니다. 에디터에서 보여주는 것은 샘플일 뿐이고, 이 때는 iframe이나 임시 div를 사용해도 됩니다. 그러나 에디터 안의 코드가 글 저장 과정에서 살아남아야 한다고 고집할 필요가 있나요? 글쓰기는 1번 하지만, 글읽기는 1000번 합니다. 에디터에서 임시로 보여준 샘플은 저장 과정에서 날아가고, 글읽기 화면에서는 글읽기에 최적화된 방식으로 본문에 다시 프리뷰를 주입하는 것이 훨씬 효율적일 수도 있어요.

  • Lv37 Lv7
    에디터에서 출력된 내용이 저장 시 날아가는 부분에 대해서는 동의합니다. 저도 애초에 그렇게 생각하고 있었구요.

    제가 의도한건 에디터에서 아이프레임(또는 ajax 등의 여러 형태라도 무방하나 외부 미디어 보안 정책에 따라 라이믹스에서 자동으로 아이프레임을 제거할테니 작성 용이)을 통해 값을 출력하며 그 시점에 캐싱이 된다는 점입니다.

    DB 쿼리든 HTTP 페칭이든 작성 시점에 처리하여 캐싱이 가능하므로, 조회 시점의 PHP단 부하는 거의 없다는거죠.

    물론 위 내용은 작성 시점 캐싱의 이야기지, 서버단에서 내려주는 형태냐 아이프레임 형태냐와는 무관한 부분입니다.

    아이프레임이 장점을 가지는 부분은...
    위에서도 언급한 레이지로딩이 가능하다는 점과 자주 갱신될 필요 없고, 중요도가 상당히 낮은 프리뷰 데이터만 별도로 클라이언트단에 캐싱할 수 있다는 점이겠네요.

    말씀하신 것 같이 HTML을 미리 컴파일하여 캐싱하고, JS에서 해당 HTML 파일을 아이프레임으로 출력하면 PHP를 단 한번도 거치지 않고 처리가 가능하고,
    그 HTML 파일을 클라이언트단 캐싱(+ 클플 캐싱)하면 게시글 조회 한번에 여러번 요청이 들어가는 문제가 발생하지 않을거구요.
    또 해당 영역이 보여지는 시점에만 아이프레임을 불러오면, 클플이 없더라도... 효과적으로 부하를 줄일 수 있을거라 생각해요.

    ...무엇보다 아이프레임은 별개의 DOM에서 동작하니 출력 환경(에디터/실제조회/불특정다수의레이아웃및스킨) 따라
    CSS 분기할 필요 없이, 수많은 !important 없이 퉁쳐도 된다는게 제일 즐거운 일이 아닐까요?!ㅎㅎㅎㅎㅎㅎㅎㅎㅎㅎㅠ
  • Lv7 Lv19
    두 분 말씀 숙독해보니 글 작성시의 캐싱과 로드시의 레이지로딩으로 부하를 줄일 수 있다는 것으로 이해가 됩니다.
    ... 그리고 클플 캐싱은 자료 이용자들의 몫이겠죠?

    개인적인 차원에서 정리해보면,

    - 그러면 캐싱할 때 이름을 어떻게 구성할 거냐
    -> 서비스명 > 유형 > 대상 url 이런 식으로 고유성을 식별할 수 있을 것 같네요. 카카오맵처럼 url이 길어지면 번거로울 수도 있겠지만 큰 지장은 없을 것도 같고요.

    - 캐시 삭제 및 재생성 시점을 언제로 볼 거냐
    -> 이거는 글 삭제 트리거와 연동하거나 하는 건 무리일 것 같고, 위젯 캐시처럼 1개월이든 1년이든 자료 이용자들이 선택하게 해서 소멸시키는 게 좋을 것 같습니다. 캐시가 없을 때 로드가 시도되면 새로 캐싱을 해주면 될 거구요.

    암튼 동적으로 url 파싱하고, 동적으로 렌더링해서 페이지 로드하고, 동적으로 아이프레임 생성해서 삽입해주고, 동적으로 캐싱하고, 또 로드시에도 동적으로 레이지하게 아이프레임 만들고 등등 온통 동적이어서 도전의식이 샘솟습니다ㅋㅋㅋ
  • Lv19 Lv37
    글 작성 시점에는 iframe.php로 링크해서 캐시 생성 + 최초 프리뷰를 제공하고, 글 읽기 시점에는 캐싱된 html 파일로 링크해 준다면 iframe을 사용해도 문제 없을 듯 합니다.

    대상 URL의 길이가 들쭉날쭉한 것은 물론, 온갖 괴랄한 특수문자가 포함되어 있을 수 있으니, 캐시 이름은 그냥 해쉬함수로 갈아버리는 것을 추천합니다.
  • Lv37 Lv19

    감사합니다.
    말씀해주신 것처럼 이렇게 경도/위도 표시하는 단위까지 들어가는 경우들이 있어서 심지어 자동링크까지도 애매해질 때까지 있는데 해시함수로 변환하면 확실히 안정적으로 코딩이 가능할 것 같습니다.

    https://www.google.com/maps/place/48°43'05.4"N+2°37'40.4"E/@48.7181667,2.6278889,17z/data=!3m1!4b1!4m4!3m3!8m2!3d48.7181667!4d2.6278889
    한번 해보겠습니다 :)