移动端防截屏录屏技术在百度账户系统实践

作者 | Seven

导读

在移动端应用的开发过程中,保护用户隐私和应用内敏感信息安全是一个不可忽视的课题。随着诈骗手段的升级,"共享屏幕"被诈骗分子频频使用,因为密码被泄露而导致受害者财物受损的事情层出不穷。只要开启了"共享屏幕"--本质上是一种录屏,密码、验证码等重要信息就会有被泄露的可能。防止截屏和录屏成为了一个重要的安全措施,特别是对于金融、医疗、企业和高安全要求的应用。本文将介绍一些在iOS和Android平台上实现防截屏和录屏的常见策略和方法,以及在百度账户系统上的实践。
全文4431字,预计阅读时间12分钟。

01 技术研究

1.1 Android平台防截屏策略

Android平台提供了一个更直接的方式来防止应用内容被截屏或录屏。Google 自 Android 4.2(API level 17)引入 FLAG_SECURE,用于将窗口内容标记为安全,禁止在屏幕截图中和非安全的显示器被输出。

php 复制代码
/** Window flag: treat the content of the window as secure, preventing
 * it from appearing in screenshots or from being viewed on non-secure
 * displays.
 *
 * <p>See {@link android.view.Display#FLAG_SECURE} for more details about
 * secure surfaces and secure displays.
 */
public static final int FLAG_SECURE             = 0x00002000;

可以在 Activity 中,通过getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE) 来设置。当截屏的时候,系统会弹出一个Toast提示"禁止屏幕抓取";当录屏的时候,当前设备显示正常且能正常操作,看似能够正常录制,但是保存后的视频,都是一片黑色,并没有APP的相关界面。

探究:FLAG_SECURE 是如何产生作用的

在 Android 图形系统上,一个 Activity 对应创建一个 Surface,每个 Surface 对应 SurfaceFlinger 中的一个 Layer。SurfaceFlinger负责管理、合成所有图层,最终显示在屏幕上。

我们通过 Android 的 源码SurfaceFlinger.cpp 和 Layer.cpp中,可以看到,activity 中设置 FLAG_SECURE 后,显示的 Surface 都是属于SECURE状态,会阻止屏幕内容被捕获。

rust 复制代码
// Call this before holding mStateLock to avoid any deadlocking.
bool canCaptureBlackoutContent = hasCaptureBlackoutContentPermission();
{   
    ...
    if (!canCaptureBlackoutContent &&
        parent->getDrawingState().flags & layer_state_t::eLayerSecure) {
        ALOGW("Attempting to capture secure layer: PERMISSION_DENIED");
        return PERMISSION_DENIED;
    }
    ...
}

1.2 iOS平台的防截屏录屏方案

在iOS平台,系统并没有直接提供API,去防止被截屏或录屏。在 iOS13之前,主要是通过监听通知,然后采取一些措施。

在 iOS7 上,当用户进行截屏时,系统会发送一个UIApplicationUserDidTakeScreenshotNotification通知。尽管我们不能阻止截屏的发生,但可以使用这一通知来采取某些措施,如模糊屏幕、显示警告或者销毁显示的敏感内容。

objectivec 复制代码
// This notification is posted after the user takes a screenshot (for example by pressing both the home and lock screen buttons)
UIKIT_EXTERN NSNotificationName const UIApplicationUserDidTakeScreenshotNotification API_AVAILABLE(ios(7.0));

在 iOS11 上,系统新增了UIScreen的API用以告知应用当前屏幕正在录屏。当UIScreen.isCaptured 为true时,表示当前屏幕正在被录制、镜像或被Airplay 发送。当录屏状态发生变化时,UIKit会发送 UIScreenCapturedDidChangeNotification 的通知。

objectivec 复制代码
// Object is the UIScreen which changed. [object isCaptured] is the new value of captured property.
UIKIT_EXTERN NSNotificationName const UIScreenCapturedDidChangeNotification API_AVAILABLE(ios(11.0)) API_UNAVAILABLE(visionos);

在 iOS13 上,UITextField 在设置 secureTextEntry 为 true 时,系统在截屏和录屏时,UITextField 所在的内容会被系统渲染为空白区域。我们可以利用这一特性,把需要视图添加到 UITextField 的子视图上(实际是私有类UITextLayoutCanvasView)来达到防止截屏和录屏的效果。

具体操作:

1、初始化一个textField,并获取其子视图 UITextLayoutCanvasView,下面称为textCanvasView

2、将textCanvasView 添加到控制器的 view 上,作为防护的底层视图

3、将所需防护的 view 添加textCanvasView 上

4、通过事件触发,开启或关闭textField的secureTextEntry属性; 相当于开启或关闭防截屏录屏

其中将防护的 view添加到 textCanvasView上,而非添加到 textField 上。因为在密码模式时,这个view会在截屏或者录屏时隐藏,同时也可以有效避免触摸事件的冲突。另外,textFiled 的secureTextEntry属性没有iOS 系统版本限制,在低版本系统上也可以设置,只是在 iOS13及后续版本上,才具备防止截屏和录屏的特性。

另一台手机录制

当前设备录制

在上面视频中红色背景为 textCanvasView,上面添加了需要防护的视图,输入密码后面的 textfield已开启 secureTextEntry。

可以看到,未开启防护时,截屏和录屏的时候,输入密码的 textfield 内容为空白;开启防护后,textCanvasView及需要防护的视图均已变为空白。

02 百度账户系统应用实践

在账号登录页,账密登录输入密码时,以及修改密码时需要设置防止截屏或录屏。

因为前端本身无法感知到系统的截屏或录屏事件,且不同的前端页面对于防护的需求也有不同,所以设计由前端来控制防护的开关,在 Android 和 iOS 上分端实现具体的防护功能。

2.1 前端应用实践

当进入到账密登录页面或修改页面时,FE 通过端方法控制打开防护,退出页面时,再由端方法来关闭防护。

双端SDK定义的一个端方法名称为 xxx_forbid_record,其中可以通过参数控制是开启或者关闭。

FE在使用时,可以直接通过 window.location.href来调用起拼接的端方法字符串 xxx://xxx_forbid_record/{"status":"1"},表示要开启防护要禁止截屏和录屏。双端 SDK分别在系统的回调中进行拦截,解析端方法,转化为调用对应原生方法。

2.2 Android实现

在基类Activity中初始化 webview时,去设置防截屏回调,可以通过参数控制 开启或关闭"禁止截屏录屏"功能。

在FE调用时,通过拦截端方法,触发"防截屏回调",进行实际控制。

如果是开启,则给 window 添加 FLAG_SCCURE 标志;如果是关闭,则通过 clearFlags 方法清除 FLAG_SCCURE标志。

调用端方法开启防护的时候,当前设备实际会提示"该应用不允许屏幕截图",且不会产生屏幕的实际截图。

录屏时,当前屏幕仍然正常显示和操作,但在录屏生成的视频中,开启防截屏的过程整个屏幕会变成黑色,关闭后防截屏后可以正常显示。

△截屏效果

2.3 iOS实现

在 百度账号 SDK 中,因为通过textfield特性来实现放截屏录屏并不是苹果系统公开的正式 API,从稳定性角度考虑,当前仅是监听了截屏和录屏的通知,通过增加弹窗,提示录屏或截屏风险。

具体实现为:

1、在webivewController中,监听系统截屏通知 UIApplicationUserDidTakeScreenshotNotification,如果是 iOS 11 及以上系统,则同时监听录屏通知 UIScreenCapturedDidChangeNotification。

2、区分录屏 或 截屏场景,进行提示。

3、FE 开启或关闭,SDK中拦截的端方法实现中,设置本地的标识为开启或关闭。

当 FE开启防截屏录屏后,SDK拦截端方法后会首先通过 [UIScreen mainScreen].isCaptured 方法,判断当前是否已经录屏。如果是,则直接弹窗提示。

当收到系统截屏或录屏的通知时,sdk 会判断满足以下两个条件都满足才会进行提示:

1)本地的标识为开启

2)当前页面正在展示

当前页面正在展示上通过 view.window 对象存在,以及 控制器的 isViewLoaded 来判断;增加此条件的判断原因是,从登录页可以打开另外一个其他的页面,在该新页面开启截屏或录屏时,登录页虽然没有显示出来,但仍会接收通知。如果新页面是不需要开启防截屏录屏,那么登录页在收到相关通知后,仅通过本地标识来判断,就出弹出不符合预期的弹窗。

在 百度账号SDK中,当进入密码登录页时,进行截屏和录屏效果如下。

截屏提示

录屏提示

03 总结

防截屏录屏功能是移动应用安全的一个关键组成部分,特别是对于处理敏感数据的应用而言。iOS 虽然未和 Android 一样有系统提供直接的 API,但上述策略和技术可以显著降低信息泄露的风险。开发者在设计应用时,需要权衡用户体验和安全需求,实施适合自己应用场景的防截屏录屏方案。

------END------

推荐阅读

AI Native工程化:百度App AI互动技术实践

揭开事件循环的神秘面纱

百度搜索展现服务重构:进步与优化

百度APP iOS端包体积50M优化实践(七)编译器优化

百度搜索内容HTAP表格存储系统 本文将介绍一些在iOS和Android平台上实现防截屏和录屏的常见策略和方法,以及在百度账户系统上的实践。

相关推荐
用户20187928316725 分钟前
滑动城堡的奇妙管家 ——ViewPager故事
android
用户20187928316725 分钟前
📜 童话:魔法卷轴与 ScrollView 的奥秘
android
??? Meggie2 小时前
【SQL】使用UPDATE修改表字段的时候,遇到1054 或者1064的问题怎么办?
android·数据库·sql
用户2018792831672 小时前
代码共享法宝之maven-publish
android
山水域2 小时前
GoogleAdsOnDeviceConversion 库的作用与用法
ios
yjm2 小时前
从一例 Lottie OOM 线上事故读源码
android·app
Lucifer晓2 小时前
记录一次Flutter项目上传App Store Connect出现“Validation failed”错误的问题
flutter·ios
用户2018792831672 小时前
浅谈View的滑动
android
用户2018792831673 小时前
舞台剧兼职演员Dialog
android
参宿四南河三3 小时前
从Android实际应用场景出发,讲述RxJava3的简单使用
android·rxjava