美文网首页阿里云
自建python伪IdP接入阿里云SSO

自建python伪IdP接入阿里云SSO

作者: 玩物技术 | 来源:发表于2020-04-17 16:02 被阅读0次

    提要: 

    本文主要讲的是一个小白(我)使用python构建一个伪IdP,实现SAML部分功能,进而接入阿里云访问控制SSO的过程。

    在正文开始前,先说一下有关SAML中的几个概念:

    统一认证中心(Indentity Provider,IdP) 此处指我方的统一登录系统

    服务提供者(Service Provider,SP) 此处指阿里云访问控制SSO

    SAML request: SP 发给 IdP要求断言的请求

    SAML response:IdP发给SP包含断言的响应

    SAML协议的核心是: IdP和SP通过用户的浏览器的重定向访问来实现交换数据(断言)。

    白话了介么多,以下就是是正文了:) 

    先说一下我方情况:已经有了一套统一登录系统。

    这个丑丑的界面就是自建的统一登录SSO - SingleSignOn 

    其实更应该叫做单点登录...

    这个系统的作用就如同ta的名字一样,作为各个系统的统一登录⻔户。只要在这一处登录,就能在接入此系统的各个地方直接获取用户信息完成登录动作。 

    当然,自己公司内部的登录,统一登录啥的可以协调一下就接入了,但是对于家大业大的阿里,咱就没法任性了,只能跟着对方的节奏走。

    接下来就是读文档了解一下阿里的接入方案,以下是阿里的文档:

    https://help.aliyun.com/document_detail/93684.html?spm=a2c4g.11186623.6.632.68f14f732BUKiO

    我方现有的使用阿里云的访问控制功能是基于用户的。每个用户配置相应的权限,且用户名与统一登录的用户名(花名拼音)是相对应的,比较适合文档中提到的用户SSO。 

    讲真,在接触阿里云的这个访问控制(RAM)的SSO之前,我真的晓不得SAML是个啥。在看了阿里云犹抱琵琶半遮面的文档之后,有了自己的理解:用XML传递断言的一个协议。

    根据下图(来源于阿里云)

    我方统一登录就是相当于IdP的⻆色。

    其具体流程梳理如下:

    1、用户Alice使用浏览器登录阿里云,阿里云(SP)将SAML认证请求(SAML request)返回给浏览器。

    2、浏览器向IdP(我方统一登录)转发SAML认证请求(SAML request)。

    3、IdP(我方统一登录)接收用户Alice发送的SAML认证请求,判断用户身份状态,是否登录:

    如果用户Alice未登录,生成login⻚面给用户浏览器,进行登录认证操作。

    如果已有登录状态,直接根据SAML request请求与Alice的身份信息生成SAML响应(SAML Response)返回给浏览器。

    4、浏览器将SAML响应转发给阿里云SSO服务。

    5、SSO服务通过SAML互信配置,验证SAML响应(SAML Response)的数字签名来判断SAML断言的真伪,并通过SAML断言的NameID元素值,匹配到对应阿里云账号中的RAM用户身份。

    6、SSO服务向浏览器返回控制台的URL。(阿里云原文是这样,但是貌似并非如此,后文再讲)

    7、浏览器重定向到阿里云控制台。

    总结下来,我方统一登录接入阿里云SSO要做的四件事就是:

    验证用户身份(本职工作,已经完成)。

    本地配置SAML IdP,阿里云配置SAML SP。

    解析SAML请求(SAML request)。

    生成SAML响应(SAML response)。

    下面我们从 2 开始(就不说从头开始,哼~) 

    一般来说,要完成IdP与SP之间的互信,需要从互传SAML的metadata开始。

    这个metadata是xml格式的。阿里云(SP)的metadata直接给提供了,在SSO的配置⻚面就能看到。

    (你看我这马赛克,是又大又匀)

    打开红框里的链接,就可以导到相应的SP的配置信息。

    一般来说,如果是通过例如Shibboleth 微软的 ADFS等现成的IdP的话,直接把这个xml添加到相应的配置中,就可以直接分析出需要的信息。

    但是本文就是要自己搞,所以我们还是康康这个xml里都有些啥吧。

    从上到下,第一个红框里的entityID ,下面的红框里的Location 

    这两个是下文构建SAML response所需要的。

    中间厚码是阿里云提供的公钥(在下文你会发现,这个公钥并没什么卵用

    )。

    entityID:

    https://signin.aliyun.com/88888888/saml/SSO

    Location:

    https://signin.aliyun.com/saml/SSO

    接下来构建我方作为IdP而提供给阿里云SP的metadata.xml。

    在metadata.xml中需要指定的内容包括:

    IdP的公钥:用于签名xml

    IdP的entityID:用于SP识别身份

    断言格式

    IdP的login url 、logout url

    在这个xml里有个很重要的内容就是密钥。

    SAML 需要密钥对交互的信息的xml文档进行签名。

    为方便大家, 这里给出一个生成X509密钥的bash脚本

    (这个脚本非原创,是从python的某个与saml相关模块里搜刮到的,具体是那个......忘记⻦)

    这里设定的过期时间是3650天就是10年,怎么也够用了。

    如果运行正常会生成两个密钥文件:

    xxxxprivate-key.pem

    xxxxcertificate.pem

    一个私钥一个公钥。

    构建metadata.xml需要的是公钥,注意,千万不要泄露私钥!

    下面是个具体示例

    从上到下

    第一个红框里是本地IdP的entityID ,这个相当于给自己的IdP起个名字

    http://my.sso.com/saml

    第二个红框指定断言格式,这个基本是固定

    <md:NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:email</md:NameIDFormat>

    第三个红框指定login 和logout的url 。

    这两个url用于接收SP发送过来SAML request 

    loginin http://my.sso.com/saml/login

    lougout http://my.sso.com/samllogout

    中部两个红色椭圆的内容是IdP公钥(太⻓,懒得打码,直接折叠起来了) 

    编辑好这个metadata.xml就可以上传给阿里云,然后打开阿里云访问控制的SSO开关

    登出阿里云子账号,然后重新访问康康会发生什么

    原本输入账号密码的登录⻚变成了只有一个 使用企业账户登录 按钮的⻚面。

    根据上文SAML的接入流程,用户点击登录后,SP(阿里云访问控制SSO)会向用户发送一个SAML request重定向,重定向的目的地是IdP的login url。

    由于是SAML request请求是经过浏览器进行重定向,所以我们完全可以通过修改本地hosts配合搭建一个本机服务来获取这个request。

    用flask可以很容易的获取这个请求。那我们来看看这个request到底是什么亚子的

    示例代码:

    编辑hosts文件,添加一行,然后运行上面这段代码

    浏览器访问阿里云的访问控制登录⻚面,点击 使用企业账号登录 看一下终端,此时SAML request就会被重定向到刚刚创建的Flask应用上了。

    如图所示:

    SAMLRequest 是SP发送请求的具体内容。

    RelayState 是在登录前访问的⻚面

    接下来的问题就是:

    根据SAML标准,这个SamlRequest是经过了deflated压缩和urlencode的xml数据。所以我们要来个逆向操作,把它还原成xml(没错全都是xml)

    示例代码如下

    将获取的SAMLrequest作为参数传递到这个方法中,得到的返回值就是原始的xml了。

    在解析过后的xml里最需要的就是本次请求的requestID。这个requestID对接下来生成SAML response很重要,可以通过xmltodict这个模块来获取requestID的具体值。

    示例代码如下

    搞定了request,下一步就是拼装response了。

    由于俺真的是不了解SAML,拼装这个respone真是要了命了,最直接的问题就是,我根本不知道他⻓什么样。

    经过与阿里云售后的沟通,拉钉钉群,售后@技术,技术无回应,等待两小时,上微博吐槽,再等待两小时后,阿里云的技术小哥哥给了我一个SAML response 示例。

    一下午时间就这么过去了,我只是想要个示例,不是核弹的设计图......怪不得别人,自己学艺不精。

    剩下的就是根据这个示例,再结合上文的配置信息,拼接成一个xml。

    xml模板如下:

    InResponseTo:上文获取的RequestID

    username:登录阿里云访问控制的用户名。

    这个用户名要符合递交给阿里云的IdP metadata中定义的NameID格式。

    以下是上文模板所用到的一些方法:生成随机ID,生成时间戳

    xml生成完毕,下一步就是对其进行签名。

    这里可以使用signxml模块来操作。

    根据其文档,要在生成签名的位置插入placeholder作为占位符

    就是上文中xml模板第八行的位置,不要直接抄signxml模块的demo,一定要指定ds的namespace。

    具体签名方法如下:

    最后一步就是将相关信息发送给SP了。

    这里要注意的是,SAML的操作都是通过浏览器重定向实现的,所以很多IdP在发送SAML response的操作实际上是生成一个自动提交的表单,表单中包含两项内容:

    SAMLResponse 其内容是base64编码的XML。

    RelayState 其内容是IdP接受到的request中的RelayState,就是登录前⻚面。

    这里我们用一个模板进行此操作。示例代码如下:

    模板文件saml_post.html

    下面给出从接受SAMLrequest到生成SAMLresponse到发送给SP的代码。

    至此,就基本实现了一个伪IdP功能。

    有没有发现什么问题?

    好像没有用到阿里云SP提供的公钥......

    但是我的功能确实实现了......

    这也是个安全隐患,如果IdP的私钥被盗,只要通过修改hosts文件就可以完成整套动作(我本地调试就是这么干的)。

    另外还记不记得上文划掉的那句话?

    SSO服务向浏览器返回控制台的URL。

    事实是,向阿里云SP发送POST请求后,他返回的真不是控制台URL,而是整个⻚面。

    数据指标体系搭建实践

    闲话画像系统实践

    为了直播效果  我们也是拼了!!!

    大家一起学安全之反弹shell

    扫描上方二维码

    更多技术干货分享

    回复【求职】海量岗位在等你~

    相关文章

      网友评论

        本文标题:自建python伪IdP接入阿里云SSO

        本文链接:https://www.haomeiwen.com/subject/qbeevhtx.html