서비스제공자의구축 - Korean Access Federation (KAFE) - 한국과학기술정보연구원
목 차 제 1 장설치환경제 2 장 simplesamlphp 의설치제 3 장 SAML 서비스제공자의설치제 4 장메타데이터의설정제 5 장웹응용과 SAML SP 의연동제 6 장보안및개인정보
SAML 서비스제공자시스템의구축 2015. 09. 09. - 초안 제 1 장설치환경 본설치매뉴얼은 simplesamlphp 1.13 버전을이용해 Ubuntu 또는 CentOS 환경에서 SAML 2.0 SP(Service Provider) 를구축하는방법을기술한다. SP 의구축을위해다음과같은요구조건이충족되어야한다. - LAMP 스택의설치 : Apache, MySQL, PHP 5.3 이상 - 공인인증서 (SSL) 의설치 제 2 장 simplesamlphp 의설치 2.1 절 simplesamlphp simplesamlphp 는 UNINETT 에서개발한 SAML v2.0 소프트웨어이다. IdP(Identity Provider) 또는 SP(Service Provider) 로설치가능하며최신버전은 1.13.2 이다 (2015 년 9 월 ). 이하 IdP 또는 SP 는 SAML 2.0 IdP 또는 SAML 2.0 SP 를의미한다. simplesamlphp.org 에서추가적인정보를얻을수있다. 2.2 절 simplesamlphp 의설치환경 simplesamlphp 의설치를위한서버환경은다음과같다. 특별한언급이없는한서비스제공자용서버는 Ubuntu 14.04 LTS(64 비트 ) 를이용한다. - php, MySQL, httpd 가설치 - IPv6 disable 권장 - selinux disable - Linux 방화벽 (iptables) 80/443(http/https) 포트개방 - 시간동기화를위한 NTP 설정 (NTP 서버 : time.kriss.re.kr) 2.3 절 simplesamlphp 의설치 simplesamlphp 의구동을위해요구되는소프트웨어패키지를설치한다. simplesamlphp 의설치경로는 /var/simplesamlphp 로가정한다. ~# clear
~# sudo apt-get install php-date openssl php5-mcrypt // 인증소스로 LDAP 를이용하는경우 ~# sudo apt-get install php5-ldap //simplesamlphp 다운로드 ~#sudo wget https://simplesamlphp.org/res/downloads/simplesamlphp-1.13.2.tar.gz // 압축해제및설치 ~# sudo cp./simplesamlphp-1.13.2.tar.gz /var/ ~# sudo cd /var ~# sudo tar zxvf./simplesamlphp-1.13.2.tar.gz ~# sudo mv./simplesamlphp-1.13.2./simplesamlphp CentOS 6.7 (php 5.5.30) 에서 mcrypt 설치방법 // 기존 php 5.5.30을모두지웠다고가정한다. ~# rpm Uvh https://mirror.webtatic.com/yum/el6/latest.rpm ~# yum install php56w php56w-opcache php56w-mcrypt php56w-xml php56w-mysql ~# service httpd restart 2.4 절 Apache 서버설정 아래설정방법은 HTTP(80 포트 ) 에대한환경설정을보여준다. HTTPS(443) 에대한 Apache 환경설정방법은생략한다. ID 제공자서버는반드시공인인증서를설치하고 HTTPS(443) 을이용해야한다. ~# sudo cd /etc/apache2/sites-available ~# sudo nano 000-default.conf // <VirtualHost *:80> 을찾아아래와같이수정 <VirtualHost *:80> DocumentRoot /var/www/html/ Alias /simplesaml /var/simplesamlphp/www ~# sudo nano /etc/apache2/apache2.conf // 다음과같은항목을추가
<Directory /var/simplesamlphp/> Options Indexes FollowSymLinks AllowOverride None Require all granted </Directory> ~# sudo service apache2 restart 2.5 절 simplesamlphp 의구동환경설정 아래와같이 simplesamlphp 를환경설정한다. 'secretsalt' 는다음명령을이용해추출할수있다. tr -c -d '0123456789abcdefghijklmnopqrstuvwxyz' </dev/urandom dd bs=32 count=1 2>/dev/null;echo ~# sudo cd /var/simplesamlphp/config ~# sudo nano config.php // 아래와같이환경설정함 'baseurlpath' => 'simplesaml/', 'certdir' => 'cert/' 'loggingdir' => 'log/', 'datadir' => 'data/', // 다음사항은꼭수정해야함 'auth.adminpassword' => '[ 관리용패스워드입력 ]', 'secretsalt' => '[secret salt 입력 ]', 'technicalcontact_name' => '[ 관리자이름 ]', 'technicalcontact_email' => '[ 관리자이메일 ]', 'language.default' => 'en', 'timezone' => 'Asia/Seoul', simplesamlphp 에서제공하는특정모듈을활성화하고싶다면 [ 모듈명 ] 디렉토리에서 enable 파일을생성한다. 다음예는 LDAP 모듈을활성화하기위한방법이다. ~# sudo cd /var/simplesamlphp/modules ~# sudo cd./ldap ~# sudo touch enable
환경설정이완료되었다면웹브라우저를이용해 http://[ 서버주소 ]/simplesaml 을접속한다. 정상적으로설치되었다면아래와같은화면이나타난다. 2.6 절설정된환경의검증 Authentication 탭에서 admin 으로로그인한다. 현재 IdP 가활성화되지않은상태이므로 2 와같이적색원이보여야한다. admin 으로로그인한후 3 과같이표시된다면정상적으로설치된상태이다. 관리자이메일과 LDAP Extension 을설치했다면모두녹색원으로표시되어야한다.
제 3 장 SAML 서비스제공자의설치 3.1 절 SSL 자가인증서의생성 아래 [myidp.mydomain.ac.kr] 은 SP 구축자의기관환경에맞게적절히변경해야한다. Common Name (e.g., server FQDN or YOUR name) 은 SP 용서버의 IP 주소또는도메인명으로설정해야한다. 자가인증서를생성한후환경설정을계속한다. ~# sudo cd /var/simplesamlphp ~# sudo mkdir cert // 생성된.cert 파일과.pem 파일은 /var/simplesamlphp/cert 디렉토리로이동 SP 의메타데이터를수정해자가인증서를등록한다. ~# sudo cd /var/simplesamlphp/config ~# sudo nano authsources.php // default-sp => array 에아래와같이수정해인증서를등록한다. 'privatekey' 'certificate' => '/var/simplesamlphp/cert/[ 인증서이름 ].pem', => '/var/simplesamlphp/cert/[ 인증서이름 ].crt', 3.2 절메타데이터설정및 IdP 메타데이터의등록 SP 의 entityid 를설정한다. entityid 는 http(s)://domain_name/sp/saml_software 의형식에따른다. ~# sudo cd /var/simplesamlphp/config ~# sudo nano authsources.php // default-sp => array 에아래와같이 entityid 값을변경한다. 'entityid' => 'https://myidp.mydomain.ac.kr/sp/simplesamlphp',
SP 와 IdP 를 ID 연계하기위해서, IdP 와 SP 간각각의메타데이터를교차등록해야한다. SP 에 IdP 의메타데이터를등록하는방법은다음과같다. IdP 의메타데이터파일을확보하고있다고가정한다. ~# sudo cd /var/simplesamlphp/metadata ~# sudo nano saml20-idp-remote.php // 아래그림 ( 예시 ) 처럼 IdP 의메타데이터를추가한다. simplesamlphp 기반의 IdP 를보유하고있다고가정했을때, http://[idp 서버주소 ]/simplesaml 에접근해 Federation 텝을클릭하면 IdP 의메타데이터정보를확인할수있다. 위그림처럼 SP 에 IdP 의메타데이터를등록하고등록된메타데이터에이름 name 을추가한다. 등록된 IdP 의메타데이터에 name 이이미등록되어있는경우에는 name 등록을생략한다. name 의내용은사용자가 SP 에서 IdP 를선택할때 (IdP discovery), 목록의형태로보여진다. ~# sudo cd /var/simplesamlphp/metadata ~# sudo nano saml20-idp-remote.php // 아래그림 ( 예시 ) 처럼 IdP의메타데이터를추가한다. $metadata[ https://[idp 주소 ]/idp/simplesamlphp ] = array( name => array( en => [IdP의이름 ], ), SP 의메타데이터도 IdP 에등록되어야 SP-IdP 간 SAML 통신이가능하다. 아래그림처럼 http://[sp 서버주소 ]/simplesaml 에접근해 Federation 탭을클릭하면 SP 의메타데이터정보를확인할수있다.
SP 의메타데이터정보를복사해서 IdP 에메타데이터등록을요청해야한다. IdP 의 /var/simplesamlphp/metadata/saml20-sp-remote.php 파일에 SP 의메타데이터를등록할수있다. simplesamlphp 는평문 (flat format) 형태의메타데이터와 XML 형태의메타데이터를함께제공한다. ID 연계되는상대 IdP 또는 SP 에자신의메타데이터를등록할때는평문형태의메타데이터를이용한다. SP 에 IdP 의메타데이터가등록되었다면아래그림과같이 http://[sp 의주소 ]/simplesaml 에접속해서 Authentication Test authentication sources 의 default-sp 를클릭한다. 아래그림과같이 Select your identity provider 에서 saml20-idp-remote.php 의 name 으로등록한이름을클릭한다. 본예시에서는 name 을 Coreen IdP guest users 로설정했다.
IdP 에등록된사용자 ID 와비밀번호를이용해로그인하면아래그림과같이 IdP 가제공하는사용자속성정보를확인할수있다.
제 4 장메타데이터의설정 4.1 절 oid to name 변환 SAML 2.0 는 Attribute 이름의 oid 표기를권장한다. ID 제공자가 oid 형태 (e.g.,urn:oid:2.5.4.4) 의속성이름을제공하고서비스제공자가 friendly name(e.g., sn) 을이용해사용자를인가한다면 oid 를 friendly name 으로변경해야한다. KAFE 는 oid 표기법을권장하므로 SP 에서다음과같이 oid2name 변환을한다. ~# cd /var/simplesamlphp/config ~# nano config.php // authproc.sp => array( 가포함된라인을찾아다음과같이추가한다. 50 => array( class => core:attributemap, oid2name, // 서비스제공자에서사용자인가를위해 friendly name o 대신에 friendly name organizationname 을이용한다면다음라인을추가한다. o => organizationname, ),
제 5 장웹응용과 SAML SP 의연동 지금부터는웹응용과 SAML SP 를연동하는방법에대해서기술한다. 5.1 절연동시고려사항 웹응용은 simplesamlphp 가제공하는 API(Application Programming Interface) 를이용해사용자를인가 (Authorization) 해야한다. KAFE(Korean Access Federation) 에서는아래표와같은속성들의이용을권장하고있다. 제공속성설명개인정보가능성 uid 사용자 ID( 시험서비스기간에한시적적용 ) O edupersontargetedid 서비스제공자별암호화된사용자고유번호 sn 성 O givenname 이름 O displayname 사용자의화면표시이름 O mail 사용자이메일주소 O edupersonaffilation organizationname schachomeorganization 사용자의기관내직무정보 사용자의소속기관명 사용자소속기관의최상위도메인이름 edupersonprincipalname 도메인내사용자 ID 정보 O edupersonscopedaffilation 도메인내사용자직무정보 IdP 가제공하는속성정보는위표에명시된속성들보다확장될수있다. 웹응용이 SAML SP 연동시고려해야할사항은다음과같다. - 사용자를구분하는방법 ; SP 는사용자를구분할수있는 ( 또는사용자충돌을피할수있는 ) 방법을준비해야한다. 다수의 IdP 에동일한사용자식별자값 ( 예 ; uid 등 ) 이존재할가능성이있다. SP 는다수의사용자속성또는메타데이터를통해얻은값들을이용해사용자를구분할수있어야한다. 5.2 절사용자인증및인가관련웹응용코드 simplesamlphp 가 /var/simplesamlphp/ 에설치되어있고 SP 로동작한다고가정한다. 또한웹응용의 root 디렉토리가 /var/www/html 이며 php 구동을위한 Apache 환경설정이완료되어있다고가정한다. IdP 에등록된계정은 student/student1234, faculty/faculty1234 이다. 다음은웹응용의 skeleton code 이다. 로그인한사용자의속성정보및사용자인증을수행한 IdP 의정보를배열의형태로얻게된다.
~# cd /var/www/html ~# nano index.php // 다음과같이추가 <?php include_once('/var/simplesamlphp/lib/_autoload.php'); $as = new SimpleSAML_Auth_Simple('default-sp'); $as->requireauth(); $attributes = $as->getattributes(); print_r($attributes);?> $idp = $as->getauthdata('saml:sp:idp'); print_r($idp); SP 가 IdP 와연계되어있고각각의메타데이터가교차등록되었을경우, http://[sp 의주소 ]/ 로접속하면아래와같이 ID 제공자를선택하는화면이나타난다. 사용자가로그인에성공할경우아래와같이사용자속성이름과속성값들이배열형태로리턴된다. // 로그인에성공한후결과값출력예시 Array( [uid] => Array( [0] => student ) [displayname] => Array( [0] => my name)... ) https://[idp의주소 ]/idp/simplesamlphp 다음은권한부여 ( 또는인가 ) 를위한예제코드이다. ID 제공자가전달한속성정보중 edupersonaffilation 이 faculty, organizationname 이 DGIST, edupersonprincipalname 이 faculty@coreen.kr 일경우에관리자권한을갖는예제이다. 사용자인가에사용할속성은서비스상황에맞게선택할수있다. ~# cd /var/www/html ~# nano index.php
// 다음과같이변경 <?php include_once('/var/simplesamlphp/lib/_autoload.php'); $as = new SimpleSAML_Auth_Simple('default-sp'); $as->requireauth(); $attributes = $as->getattributes(); $idp = $as->getauthdata('saml:sp:idp'); $uid = $attributes['uid'][0]; $displayname = $attributes['displayname'][0]; $mail = $attributes['mail'][0]; $edupersonaffilation = $attributes['edupersonaffiliation'][0]; $organizationname = $attributes['organizationname'][0]; $schachomeorganization = $attributes['schachomeorganization'][0]; $edupersonprincipalname = $attributes['edupersonprincipalname'][0]; $edupersontargetedid = $attributers['edupersontargetedid'][0]; //authorization if ($edupersonaffilation === 'faculty' && $organizationname === 'DGIST' && $edupersonprincipalname === 'faculty@coreen.kr'){ $isadmin = 1; }else{ $isadmin = 0; } if($isadmin){ echo "Welcome Prof. ".$displayname."!!<br>"; echo "You are allowed to access IT resource 1, 2, and 3."; }else{ echo "Welcome Student ".$displayname."!!<br>";
echo "You are allowed to access IT resource 1 only."; }?> 5.3 절로그인및로그아웃 다음은 SURFnet 에서제공하는 simplesamlphp 용 SP 의예제코드이다. ID 연계를위한 SAML 메시지중개시스템인 OpenConext 와연동해보다강력한사용자인증을수행하기위한코드이다. SURFnet 의 ID 연계구조는 Hub&spoke 이지만 KREONET 의 KAFE 는 Full mesh 구조를갖기때문에메시지중개시스템을이용하지않는다. 아래코드의 LOA(Level Of Assurance) 관련부분은 KAFE 에서사용되지않는다. //source code provided by SURFnet //https://wiki.surfnet.nl/display/suaas/configuring+a+simplesamlphp+sp+for+step-up+authentication <?php // Include SimpleSAMLphp. Assume this script is placed in the <simplesaml>/www dir. require_once('../lib/_autoload.php'); // Name of session variable for storing the min required LOA(Level of Assurance) for a login define( 'SSP_SESSION_MIN_LOA', 'RequestedMinLOA' ); // Build return URL. This is where ask simplesamlphp to direct the browser to after login or logout // Point to this script, but without any request parameters so we won't trigger an login again (and again, and again, and...) $returnurl = ($_SERVER['HTTPS'] == 'on')? 'https://' : 'http://'; $returnurl.= $_SERVER['HTTP_HOST']; $returnurl.= $_SERVER['SCRIPT_NAME']; // Map integer level of assurance level to identifier used by the gateway $gloamap = array( 1 => 'http://suaas.example.com/assurance/loa1', 2 => 'http://suaas.example.com/assurance/loa2', 3 => 'http://suaas.example.com/assurance/loa3', );
try { // Init SP instance // Assumes you have setup a SP named "default-sp" in <simplesaml>/config/authsources.php // See: https://simplesamlphp.org/docs/stable/simplesamlphp-sp $as = new SimpleSAML_Auth_Simple('default-sp'); // Init SP instance /** @var $session SimpleSAML_Session */ $session = SimpleSAML_Session::getInstance(); // Process login action. Assumes the login function of your SP uses...?action=login if (isset($_request['action']) && $_REQUEST['action'] == 'login' ) { // We use the SSP session to keep track of the LOA we want. // Unset any existing RequiredAuthnContextClassRef $session->deletedata('string', SSP_SESSION_MIN_LOA); // login $requiredloa = 2; // The LOA we want. // Store the requested LOA in the session so we can verify it later $session->setdata('string', SSP_SESSION_MIN_LOA, $requiredloa); $as->login( array( 'ReturnTo' => $returnurl, 'ForceAuthn' => false, 'saml:authncontextclassref' => $gloamap[$requiredloa] // Specify LOA ) ); exit; // Never reached. Added for clarity } // Process logout action if( isset($_request['action']) && $_REQUEST['action'] == 'logout' ) { $as->logout( array (
'ReturnTo' => $returnurl, ) ); // Process logout exit; // Never reached. Added for clarity } // Display HTML page echo <<<head <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <style type="text/css"> table,th,td {border: 1px solid black;} th,td {padding 1px} </style> <title>simplesamlphp Demo</title> </head> <body> <h1>simplesamlphp LOA Demo</h1> head; // Show some info when authenticated if ( $as->isauthenticated() ) { $attributes = $as->getattributes(); $requestedloa = $session->getdata('string', SSP_SESSION_MIN_LOA); // What we requested during login $authstate = $session->getauthstate(); $authnconext = $authstate['saml:sp:authncontext']; $nameid = $session->getnameid(); $authninstant = gmdate('r', $authstate['authninstant'] );
$expire = gmdate('r', $authstate['expire'] ); echo "<h2>you are logged in</h2>"; echo "<h3>simplesamlphp Session</h3>"; echo "<p>simplesamlphp session start: <b>{$authninstant}</b></br />"; echo "SimpleSAMLphp session expire: <b>{$expire}</b></p>"; echo "<h3>loa</h3>"; echo "<p>received authnconext: <b>{$authnconext}</b></p>"; // Map LoA identifier back to integer LoA level $actualloa = array_search($authnconext, $gloamap); if (false ==! $actualloa) echo "<p>actual LoA is: <b>{$actualloa}</b></p>"; else $actualloa = -1; if (NULL!== $requestedloa) { echo "<p>requested LoA was: <b>{$requestedloa}</b></p>"; if ($actualloa >= $requestedloa) echo '<p><b>you were authenticated at or above the minimally required LoA</b></p>'; else echo '<p><b>you were NOT authenticated at the required LoA</b></p>'; } echo <<<html <h3>nameid</h3> <table> <tr><th>value</th><td>{$nameid['value']}</td></tr> <tr><th>format</th><td>{$nameid['format']}</td></tr> </table>
html; echo <<<html <h3>saml Attributes</h3> <table> <tr><th>attribute</th><th>value(s)</th></tr> html; foreach ($attributes as $attrname => $attrval) { echo "<tr><td>{$attrname}</td><td>"; if (is_array($attrval)) echo implode('<br />', $attrval); else echo $attrval; echo "</td>"; } echo <<<html </table> <h3>logout</h3> <p> <form name="logout" action="{$returnurl}" method="get"> <input type="hidden" name="action" value="logout"/> <input type="submit" value="logout" /> </form> </p> html; } else { echo <<<html <h2>your are not logged in</h2> html; }
echo <<<html <h3>login (again)</h3> <p> <form name="login" action="{$returnurl}" method="get"> <input type="hidden" name="action" value="login"/> <input type="submit" value="login" /> </form> </p> html; echo <<<html </body> </html> html; } catch (Exception $e) { echo $e->getfile().':'.$e->getline().' : '.$e->getmessage(); }
제 6 장보안및개인정보 6.1 절페이지접근제어 simplesamlphp 를설치하면일반사용자가웹브라우저를통해 IdP 또는 SP 의 metadata, phpinfo 등보안정보에접근할가능성이있다. simplesamlphp 를설치한후해당정보들을은닉하기위해 config.php 파일을수정해야한다. default themes 의 userloginpass.php 파일을수정해사용자인터페이스를변경할수있다. ~# clear ~# cd /var/simplesamlphp/config/config.php // 아래와같이 protectindexpage 와 protectmetadata 값을 true 로변경한다. 'admin.protectindexpage' => true, 'admin.protectmetadata' => true, SSP 의관리자페이지노출취약점을해결하기위해 apache 설정을변경해줘야한다. CentOS 6.5 기준으로 SP 가 SSL 이적용되어있을때다음과같이설정한다. ~# clear ~# cd /etc/httpd/conf.d ~# nano ssl.conf //</VirtualHost> 앞에다음과같이추가한다. 설치환경에맞게적절히수정되어야한다. <Location /simplesaml/module.php/core/loginuserpass.php> Order Deny,Allow Deny from all # 192.168.0.* 만접속가능 Allow from 192.168.0.0/24 </Location> 6.2 절 Privacy Policy SP 의메타데이터에 privacypolicy 가설정되어있으면 consent 에서해당 privacypolicy 를링크한다. Consent 화면에는 privacypolicy 의 URL 이 %SPENTITYID% 로변경되어표시된다. 서비스제공자는 ID 제공자에게 privacypolicy URL 정보를전달하고, ID 제공자가 privacypolicy 정보를설정해야한다. ~# clear ~# nano /var/simplesamlphp/metadata/saml20-sp-remote.php // 특정 SP 의 metadata 내에
'privacypolicy' => URL, // 예 ; privacypolicy => https://yourdomain.com/privacypolicy, // 이설정되어있으면 consent 시해당 URL 이화면출력됨 6.3 절 SSP 의보안강화사항 showerrors 항목을 false 로해서오류가발생했을때노출되지않아야할오류정보 (stacktrace 는시스템정보를노출함 ) 가사이트에노출되는것을방지한다. 또한, admin 비밀번호를설정해 SSP 의정보가노출되지않도록한다. ~# nano /var/simplesamlphp/config/config.php //5.1과동일 'admin.protectindexpage => true, // 추가또는수정 showerrors => false, 쿠키보안을위해, 평문연결 (plain text connection, Non-TLS) 일때쿠키정보가전송되는것을막고자바스크립트가쿠키에접근하는것을막아야한다. TLS(https connection) 를반드시이용해야한다. 쿠키보안설정을하지않으면 Cross Site Scripting 공격에취약할수있다. ~# nano /var/simplesamlphp/config/config.php // 다음과같이수정 'session.cookie.secure => true, session.phpsession.httponly => true, SSP 가 redirect 를할도메인이름을설정한다. 다음과같이 empty array 로설정하면 SSP 가자동으로신뢰하는도메인으로만 redirect 한다. ~# nano /var/simplesamlphp/config/config.php // 다음과같이수정 trusted.url.domains => array(), SHA1( 보안취약 ) 대신 SHA-256 으로이용한다. ID 제공자는 saml20-idp-hosted.php 를, SP 제공자는 authsources.php 를수정한다. SSP 1.12 이상의버전에서는 config 파일에반영되어있으므로주석표시만제거한다.
// ID 제공자일경우에해당 ~# nano /var/simplesamlphp/metadata/saml20-idp-hosted.php // 다음과같이주석제거 'signature.algorithm' => 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256', [ 최근갱신 : 2015-11-6 draft v0.14]