WebView의 전자서명은 어디까지 믿을 수 있을까
plainData와 signedData, 실제 업무 요청을 함께 바라보며
WebView에서 전자서명을 연동하는 구조를 처음 검토했을 때는 비교적 단순하게 생각했습니다.
웹에서 서명할 데이터를 만들고 앱 브릿지를 호출합니다. 앱이 전자서명을 수행해 signedData를 돌려주면 웹이 서버로 전달하고, 서버에서 서명을 검증하면 된다고 보았습니다.
그런데 구조를 계속 들여다보면서 몇 가지 질문이 남았습니다.
앱에서 서명에 성공했다면 정말 끝일까?
사용자가 서명한 내용과 서버가 처리할 요청은 같은 데이터일까?
서버는 클라이언트가 전달한 값 중 어디까지 믿을 수 있을까?
서명 성공과 업무 검증은 달랐습니다
전자서명 검증이 성공했다는 것은 주어진 원문에 대해 유효한 서명이 생성되었다는 의미입니다.
하지만 그 원문이 서버가 실제로 처리할 업무 요청과 같은지는 별개의 문제였습니다.
WebView에서 생성한 plainData, 앱에서 만들어진 signedData, 서버로 전달되는 업무 요청 데이터는 서로 다른 경로를 지나갑니다. 세 값이 언제나 같을 것이라고 가정하면 중간 구간에서 값이 달라지는 상황을 확인하기 어렵습니다.
앱 브릿지는 웹과 앱을 연결하는 중요한 통로지만, 브릿지를 통과했다는 사실만으로 업무 데이터의 신뢰성이 완성되는 것은 아니었습니다.
원문을 어디에서 만들 것인지부터 다시 보았습니다
WebView가 서명 원문을 모두 만드는 방식은 화면 구현이 빠르고 유연합니다.
반면 서버가 모르는 형태의 원문이 만들어질 수 있고, 실제 업무 요청과 비교하기 위해 같은 조립 규칙을 서버에도 유지해야 합니다. 화면마다 원문 형식이 달라지면 검증 기준도 복잡해집니다.
서버가 업무 요청을 기준으로 서명 대상 데이터를 만들고 식별자를 발급하는 방식은 신뢰 기준을 서버에 둘 수 있습니다. 다만 서명 전후의 흐름과 상태 관리가 추가되고, 화면과 서버 사이의 계약을 더 명확하게 정의해야 합니다.
이번 프로젝트에서는 어느 위치에서 원문을 만들든 서버가 최종 비교 기준을 가져야 한다고 보았습니다.
서버가 다시 확인해야 할 것
서버는 signedData의 암호학적 유효성만 확인해서는 충분하지 않았습니다.
검증을 통해 얻은 서명 원문이 서버가 기대한 원문과 같은지 확인해야 합니다. 계좌, 상품, 금액, 업무 구분처럼 실제 처리에 영향을 주는 값이 서명 대상과 업무 요청에서 일치하는지도 비교해야 합니다.
서명이 현재 요청을 위해 발급된 것인지, 이미 사용된 요청이 아닌지, 허용된 시간 안에 처리되는지도 함께 봐야 합니다.
클라이언트가 전달한 plainData를 비교 기준으로 다시 신뢰하는 것이 아니라 서버가 보관하거나 재구성할 수 있는 값과 비교하는 것이 필요했습니다.
전자서명을 하나의 흐름으로 보게 되었습니다
전자서명은 브릿지 호출의 성공 여부를 확인하는 기능이 아니었습니다.
사용자가 확인한 내용, 실제로 서명한 원문, 서버가 처리할 업무 요청이 하나의 흐름으로 이어지고 있는지 검증하는 일이었습니다.
앱은 안전한 서명 기능을 제공하고, 웹은 사용자에게 내용을 보여주며 서명 흐름을 연결합니다. 서버는 전달받은 결과를 기반으로 실제 업무를 처리해도 되는지 최종 판단합니다.
전자서명을 어디까지 믿을 수 있는지에 대한 답은 특정 구간을 신뢰하는 데 있지 않았습니다.
각 구간이 만든 결과를 서버의 업무 기준으로 다시 연결해 확인할 수 있을 때, 비로소 서명과 업무 처리가 같은 요청이었다고 말할 수 있었습니다.