热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

二、信息:一个应用的基础

二、信息:一个应用的基础所有有意义的应用的基础是信息,我们设计和构建应用来交换、创建或存储信息。

二、信息:一个应用的基础

所有有意义的应用的基础是信息,我们设计和构建应用来交换、创建或存储信息。移动应用也不例外。在当今连接良好的移动领域,信息交换是游戏的名称。为了说明这一点,想象一部没有移动网络或 WiFi 覆盖的 Android 手机。虽然这种手机仍有用武之地,但你将无法访问手机上一些更重要的应用。例如,电子邮件、即时消息、网页浏览和任何其他需要互联网的应用现在都将无法运行。

在后面的章节中,我们将集中精力检查传输中的信息以及如何保护它。在本章中,我们将主要关注存储的信息发生了什么变化。

保护您的应用免受攻击

当创建或接收数据时,数据需要存储在某个地方。这些信息的存储方式最终将反映出您的应用的安全性。向公众发布你的应用应该像在互联网上建立一个网站一样小心谨慎。您应该假设您的应用在某个时候会受到直接或间接的攻击,并且您的应用是最终用户隐私和数据保护之间的唯一障碍。

间接攻击

尽管最后一句话听起来很有戏剧性,但它并非毫无根据。在我们进一步讨论之前,让我们看看我散布恐惧是否有道理。2010 年后期和 2011 年初,分别在 Android 和 2.3 版本中发现了两个漏洞。该漏洞本质上是相同的,在该漏洞中,攻击者可以复制存储在设备 SD 卡上的任何文件,而无需许可,甚至没有可见的提示。该漏洞的工作原理如图图 2-1 所示。

9781430240624_Fig02-01.jpg

图 2-1 。数据窃取漏洞

以下是最值得注意的几点:


  1. 用户访问托管文件的恶意网站,如 evil.html。

  2. 由于漏洞的一部分,在没有提示用户的情况下,evil.html 文件被下载并保存到设备 SD 卡。

  3. 由于该漏洞的另一部分,保存的文件可以在保存后立即执行 Javascript 代码。同样,对最终用户没有提示。

  4. 由于此漏洞的最后一部分,前面执行的 Javascript(因为它运行在设备的“本地”上下文中)将完全有权将存储在 SD 卡上的文件上传到攻击者选择的网站。

为了便于讨论,假设您的应用将所有保存的信息写入 SD 卡,存储在它自己的目录下。由于刚才讨论的漏洞,您的应用使用的数据有被窃取的风险。任何运行您的应用和易受攻击的固件版本的 Android 设备都会给其最终用户带来数据被盗的风险。这是对您的应用进行间接攻击的一个例子。

您的应用对于间接攻击的脆弱性很大程度上取决于您在开始编写一行代码之前,在设计考虑安全方面方面投入了多少努力。你可能会问这样的问题,“我只是一个计划在网上低价出售我的应用的小应用开发者,所以我真的需要浪费时间事先做这么多计划吗?”我会响亮地回答你:“是的!”无论您是 30 名开发人员团队的一员,还是在家工作的个人,一个架构良好的应用都是您应该努力创建的。我希望这是你将从本书中学到的东西。

直接攻击

直接攻击明显不同,可以采取多种不同的形式。直接攻击可以分为直接针对您的应用的攻击。因此,攻击者希望利用应用设计中的弱点来收集应用用户的敏感信息,或者攻击应用与之对话的服务器。以一个移动银行应用为例。攻击者可能会追踪属于特定银行的移动应用。如果应用设计薄弱 — 例如,如果敏感的用户数据以明文形式存储,或者应用和服务器之间的通信没有受到 SSL — 的保护,那么攻击者可以专门针对这些弱点发起攻击。这是对特定应用的直接攻击。我将在本书第 9 章的中更详细地讲述直接攻击。

项目 1:“Proxim”和数据存储

让我们从一个名为 Proxim 的简单例子开始。我已经签约编写一个应用,当用户在一组 GPS 坐标的一定范围内时,该应用可以向特定的、已定义的联系人发送 SMS。例如,使用这个应用,用户可以将他的妻子添加为联系人,并且每当他在他的工作场所和家的三英里范围内时,就让应用给她发短信。这样,她就知道他什么时候离家和办公室近了。

您可以从 Apress 网站的源代码/下载区域(【www.apress.com】)下载并检查 Proxim 应用的整个源代码。为了清楚起见,让我们看一下最重要的区域。

数据存储程序如清单 2-1 中的所示。

清单 2-1 。 保存例程,SaveController。java

包 net . Zen consult . Android . controller;

导入 Java . io . file;

导入 Java . io . file notfounindexception;

导入 java.io.FileOutputStream:

导入 java . io 异常:

导入 net . Zen consult . Android . model . contact;

导入 net . Zen consult . Android . model . location;

导入 Android . content . context;

导入 Android . OS . environment;

导入 android.util.Log:

公共类 SaveController {

私有静态最终字符串标签= " save controller ";

公共静态 void saveContact(上下文 Context,Contact contact) {

if ( isReadWrite ()) {

尝试{

File output File = new File(context . getexternalfilesdir(null),contact . get first name());

FileOutputStream outputStream = new FileOutputStream(outputFile);

output stream . write(contact . getbytes());

output stream . close();

} catch(file notfounindexception e)}

日志。 e ( 标签,“找不到文件”);

} catch(io exception e)}

Log. e ( TAG ,"IO Exception");

}

} else {

日志。 e ( TAG ,“读写模式下打开媒体卡出错!”);

}

}

公共静态 void saveLocation(上下文上下文,位置位置){

if ( isReadWrite ()) {

尝试{

File outputFile = new File(context.getExternalFilesDir(null),location.getIdentifier());

FileOutputStream outputStream = new FileOutputStream(outputFile);

output stream . write(location . getbytes());

output stream . close();

} catch(file notfounindexception e)}

日志。 e ( 标签,“找不到文件”);

} catch(io exception e)}

日志记录。 e ( 标记,“IO 异常”);

}

} else {

日志。 e ( TAG ,“读写模式下打开媒体卡出错!”);

}

}

私有静态布尔值 isReadOnly() {

日志。 e ( 标记,环境

.get xternalstoragestate);

回归环境。媒体装载只读。等于(环境

.get xternalstoragestate);

}

私有静态布尔 isReadWrite() {

日志。 e ( 标记,环境

.get xternalstoragestate);

回归环境。介质安装。等于(环境

.get xternalstoragestate);

}

}

每次用户选择“保存位置”按钮或“保存联系人”按钮时,都会触发前面的代码。让我们更详细地看看位置(见清单 2-2 )和联系人(见清单 2-3 )类。虽然我们可以实现一个主要的保存例程,但我将它分开,以防需要以不同的方式对不同的对象进行操作。

清单 2-2 。Location.java 外景班

包 net . Zen consult . Android . model;

publicclass location

私有字符串标识符;

私人双 latitude

私人双倍长度;

公共位置(){

}

publicdouble get attitude()±

返回纬度;

}

publicvoid setLatitude(双纬度){

this.latitude =纬度;

}

public double get length()= & gt

返回经度;

}

publicvoid setLongitude(双经度){

this.lOngitude=经度;

}

publicvoid setidentifier(字符串标识符)

this.identifier =标识符;

}

public string getiidentificar()= & gt

返回标识符;

}

公共字符串 toString() {

string builder ret = new string builder();

ret . append(get identifier());

ret . append(string . value of(get distance()));

ret . append(string . value of(get length()));

return ret.toString():

}

公有字节[] getBytes() {。

返回到 String().getBytes();

}

}

清单 2-3 。【Contact.java】的触点类,的

net . Zen consult . Android . model;

publicclass contact

串名;

私有字符串姓氏;

私有字符串 address1

私有字符串 address2

私人字符串邮件;

私人串线电话;

公共联系人(){

}

public 字符串 getFirstName()>

返回名字;

}

public voidset first name(String first name){

这个。firstName =名字;

}

publicString get last name(){

返回姓氏;

}

public voidset last name(String last name){

这个。lastName =姓氏;

}

public 字符串 get ddress 1()= >

返回地址 1;

}

publicvoid setAddress1(字符串地址 1) {

this. address 1 = address 1;

}

public 字符串 getaddress 2()>

返回address 2;

}

publicvoid setAddress2(字符串地址 2) {

这个. address 2 = address 2;

}

public 字符串 getemail()>

回复邮件;

}

发布【set email(string email)】

这个。email = email

}

public String getPhone() {

返回电话;

}

public voidset phone(String phone){

这个。电话=电话;

}

public String toString() {

StringBuilder ret = string builder();

ret . append(get first name()+|);

ret . append(get astname()+|);

ret . append(get address 1()+|);

ret . append(get address 2()+|);

ret . append(get mail()+|);

ret . append(get microphone()+|);

返回ret . tostring();

}

公有字节[] getBytes() {。

返回tostring()getbytes();

}

}

位置和联系类是标准类,用于保存特定于每种类型的数据。它们中的每一个都包含了 toString() 和 getBytes() 方法,这些方法将类的全部内容作为一个字符串或一个字节数组返回。

如果我们要手动添加一个联系人对象,那么我们很可能会使用类似于清单 2-4 中所示的代码。

清单 2-4 。 代码,增加一个新的联系对象

期末联系人= 联系人();

contact . set first name(" Sheran ");

contact . set last name(" Gunasekera ");

contact.setAddress1(" ")

contact.setAddress2(" ")

contact . set email(" sheran @ Zen consult . net ");

contact . set phone(" 12120031337 ");

现在假设当用户填充屏幕向应用添加新联系人时,调用清单 2-4 中的代码。您将使用显示在主视图上的每个 EditText 对象的 getText() 方法,而不是看到硬编码的值。

如果您在您的 Android 模拟器中执行代码 save controller . save Contact(getApplicationContext()、Contact)), SaveController 将获取新创建的联系人并将其存储在外部媒体源中(回头参考清单 2-1 )。

注意使用 getExternalFilesDir() 方法在 Android 设备上查找 SD 卡的位置始终是一个好的做法。因为 Android 可以在大量不同规格的设备上运行,所以 SD 卡目录的位置可能并不总是在 /sdcard 。 getExternalFilesDir() 方法将向操作系统查询 SD 卡的正确位置,并将该位置返回给您。

让我们一次看一行,从 saveContact() 方法的构造函数开始:

public static void saveContact(Context context, Contact contact) {
if (*isReadWrite*()) {
try {

前面的代码片段需要一个上下文对象和一个联系人对象。Android 上的每个应用都有自己的环境。一个上下文对象包含应用特定的类、方法和资源,它们可以在应用中的所有类之间共享。例如,上下文对象将包含关于 SD 卡目录位置的信息。要访问它,您必须调用 context . getexternalfilesdir()方法。该方法接受参数后,将检查设备上的 SD 卡是否已安装,以及是否可写。 isReadWrite() 方法将执行并返回一个真或假值来表明这一点:

File outputFile = new File(context.getExternalFilesDir(null),contact.getFirstName());

这段代码创建了一个指向 SD 卡目录位置的文件对象。我们使用联系人对象的名字作为文件名:

FileOutputStream outputStream = new FileOutputStream(outputFile);
outputStream.write(contact.getBytes());
outputStream.close();

使用这段代码,我们创建了一个指向我们的文件对象的位置的文件输出流。接下来,我们使用 getBytes() 方法将联系人对象的内容写入输出流,以返回一个字节数组。最后,我们关闭文件输出流。

当执行完成时,我们应该有一个名为“Sheran”的文件写入设备上的 SD 卡目录。我在 Mac OS X 雪豹上使用安卓模拟器。因此,当我导航到模拟器的位置时,我可以看到如图图 2-2 所示的屏幕。

9781430240624_Fig02-02.jpg

图 2-2 Max OS X 上的 SD 卡镜像文件 T5

当通过导航到 Android/data/net . Zen consult . Android/files 来挂载该图像时,新创建的联系人文件名可见(参见图 2-3 )。

9781430240624_Fig02-03.jpg

图 2-3 被写入文件的联系对象

如果我们在文本编辑器中打开文件,我们可以看到从应用中保存的纯文本数据(见图 2-4 )。

9781430240624_Fig02-04.jpg

图 2-4 。联系对象的内容

信息分类

当我开始从事移动应用开发时,我所面临的一个问题是,我必须从一开始就开始编写代码。我会在脑海中构思这些特征,并在进行过程中编写代码。很多时候,我会花时间修改我的代码,然后中途回去写一个计划。这对我的截止日期和交付成果产生了毁灭性的影响。这也对我的应用的安全性产生了不利影响。

从那以后,我认识到写一份我即将着手的项目的简要大纲将有助于我提前考虑事情。虽然这似乎是一件显而易见的事情,但我所接触过的许多开发人员都没有遵循这个简单的步骤。我也开始认真做的另一件事是找时间查看我的应用将要处理的信息或数据。例如,我使用一个类似于表 2-1 中所示的表来对我的应用处理的数据进行分类。桌子很基础;然而,通过将它写在纸上,我能够想象我的应用将处理的数据类型,而且,我能够制定一个计划来保护这些信息。

表 2-1 。数据分类表



















































数据类型私人的?敏感?创造商店发送接收
名字XXx
电子邮件地址XXx
电话号码XX
地址XX

如果你仔细看一下表 2-1 中的数据分类表,你会发现有些标题非常主观。不同的人对什么是敏感信息或个人信息会有不同的看法。然而,通常最好是尝试并集中在一个共同的参考框架上,以确定什么是敏感信息和个人信息。在本节中,您将首先查看表格标题,然后查看每一列:


  • 数据类型:您将在您的应用中处理这些数据。这是不言自明的。

  • 个人?:此栏表示数据类型是否归类为个人信息。

  • 敏感?:此栏表示数据类型是否属于敏感信息。

  • Create :您的应用允许这个用户创建这个数据类型吗?

  • Store :您的应用将这种数据类型存储在设备上还是远程服务器上?

  • Sent :这种数据类型是通过网络发送给另一方还是服务器?

  • 接收:这种数据类型是通过网络从另一方接收的吗?

什么是个人信息?

个人信息可以归类为你和你的社交圈内有限数量的人所知道的数据。个人信息通常是你的隐私,但你愿意与亲密的朋友和家人分享。个人信息的例子可以是您的电话号码、地址和电子邮件地址。泄露这些信息通常不会对你或你的家庭成员造成严重的身体或精神伤害。相反,它可能会给你带来极大的不便。

什么是敏感信息?

敏感信息比个人信息更有价值。敏感信息通常是您在大多数情况下不会与任何人共享的信息。这类数据包括您的密码、网上银行凭证(如 PIN 码)、手机号码、社会保险号或地址。如果敏感信息被泄露,那么后果可能会给你带来身体或精神上的伤害。无论信息是在传输中还是在存储中,都应该始终受到保护。

警告敏感信息的丢失会对您的身体或情感造成怎样的伤害?考虑丢失您的网上银行凭证。攻击者会偷走你所有的钱,给你造成巨大的经济(身体和情感)损失。跟踪者掌握了你的电话号码或地址,会对你或你家人的身体健康造成严重威胁。

代码分析

如果我们回到本章前面讨论的间接攻击,很明显,在 SD 卡上清晰可见地保存数据是一个巨大的风险,应该不惜一切代价避免。数据失窃或泄露已经成为企业财务和声誉损失的主要原因之一。但是,仅仅因为你为智能手机的单一用户编写应用,并不意味着你应该对数据盗窃掉以轻心。就 Proxim 而言,明文数据存储的这一弱点是存在的。任何能够访问该设备 SD 卡的人都可以复制个人信息,如姓名、地址、电话号码和电子邮件地址。

我们可以追踪原始代码中的缺陷,直到我们保存数据的地方。数据本身没有以任何方式隐藏或加密。如果我们加密数据,那么个人信息仍然是安全的。让我们看看如何在我们的原始 Proxim 代码中实现加密。第 5 章将深入讨论公钥基础设施和加密;因此,出于这个练习的目的,我们将介绍一个非常基本的高级加密示例标准(AES)加密。公钥加密或非对称加密是一种通过使用两种不同类型的密钥来加密或混淆数据的方法。每个用户有两个密钥,一个公钥和一个私钥。他的私钥只能解密由公钥加密的数据。这个密钥被称为公共密钥,因为它是免费提供给其他用户的。其他用户将使用这个密钥来加密数据。

在哪里实现加密

我们会在将数据保存到 SD 卡之前对其进行加密。这样,我们就永远不会以任何人都可以读取的格式将数据写入 SD 卡。收集您的加密数据的攻击者必须首先使用密码来解密数据,然后才能访问它。

我们将使用 AES 通过密码或密钥加密我们的数据。加密和解密数据都需要一个密钥。这也称为对称密钥加密。与公钥加密不同,该密钥是唯一用于加密和解密数据的密钥。这个密钥需要安全地存储,因为如果它丢失或泄露,攻击者可以用它来解密数据。清单 2-5 显示了加密程序。

清单 2-5 。 一个加密例程

私有字节【加密】字节键、字节数据{。

SecretKeySpec =newSecretKeySpec(key," AES ");

密码密码;

字节[]塞浦路斯文本=null;

试试 {

密码=密码。getInstance【AES】;

cipher.init(密码。 ENCRYPT_MODE ,sKeySpec);

密文= cipher.doFinal(数据);

}catch(nosuchin 算法异常 e)}

日志。 e ( 标签,“nosuchalgrimetricexception”);

}catch(nosuchtpaddingexception e)}

日志。 e ( 标签,“NoSuchPaddingException”);

} catch (非法块阻止异常 e)}

日志。 e ( 标签,“IllegalBlockSizeException”);

}catch(badpaddingexception e)}

日志。 e ( 标签,“BadPaddingException”);

}catch(invalidkeyexception e)>

日志。 e ( 标签,“InvalidKeyException”);

}

返回密文;

}

让我们一段一段地检查代码。第一位代码初始化 SecretKeySpec 类,并创建一个新的密码类实例,为生成 AES 密钥做准备:

SecretKeySpec sKeySpec = new SecretKeySpec(key,"AES");
Cipher cipher;
byte[] ciphertext = null;

前面的代码还初始化了一个字节数组来存储密文。下一位代码为密码类使用 AES 算法做准备:

cipher = Cipher.*getInstance*("AES");
cipher.init(Cipher.*ENCRYPT_MODE*, sKeySpec);

cipher.init() 函数初始化密码对象,因此它可以使用生成的密钥执行加密。下一行代码加密纯文本数据,并将加密的内容存储在密文字节数组中:

ciphertext = cipher.doFinal(data);

为了让前面的例程工作,它应该总是有一个加密密钥。重要的是,我们对解密程序使用相同的密钥。否则就会失败。通常最好编写自己的密钥生成器,它将生成一个基于随机数的密钥。这将使攻击者比普通密码更难猜到。在这个练习中,我使用了清单 2-6 中所示的密钥生成算法。

清单 2-6 。 一种密钥生成算法

publicstatic byte[]generate key(byte[]randomained)>

SecretKey sKey = null

试试 {

key generator keygen = key generator。getinstance(" AES ");

securerandom = securerandom。getinstance(" sha 1 prng ");

random . setseed(randomNumberSeed);

keyGen.init(256,随机);

skey = key gen . generate key();

}catch(nosuchin 算法异常 e)}

日志。 e ( 标签,“无此类算法异常”);

}

returnskey . get encoded();

}

现在,让我们分析代码。这两行代码初始化 KeyGenerator 类,这样它就可以生成特定于 AES 的密钥,然后初始化设备的随机数生成器,这样它就可以生成随机数:

KeyGenerator keyGen = KeyGenerator.*getInstance*("AES");
SecureRandom random = SecureRandom.*getInstance*("SHA1PRNG");

这些随机数用 SHA1 编码。SHA1 或安全哈希算法 1 是一种加密哈希函数。该算法将对具有任意长度的一段数据进行操作,并将产生固定大小的短字符串。如果被散列的数据的任何部分被改变,那么产生的散列将会变化。这表明一部分数据已经被篡改。

下一段代码使用提供的随机数种子,通过这个随机数生成一个 256 位密钥:

random.setSeed(randomNumberSeed);
keyGen.init(256,random);
sKey = keyGen.generateKey();

只需运行一次密钥生成算法,并保存生成的密钥以供解密例程使用。

加密的结果

当我们检查 SD 卡中的同一个联系人对象时,内容出现乱码(见图 2-5 ),任何不经意的窥探者或蓄意攻击者都无法读取。

9781430240624_Fig02-05.jpg

图 2-5 联系对象的加密内容

返工项目 1

我们对 Proxim 项目的更改主要影响了 saveController() 方法(参见清单 2-7 )。

清单 2-7 。T5【返工 SaveController.java】法

包 net . Zen consult . Android . controller;

导入 Java . io . file;

导入 Java . io . file notfounindexception;

导入 java.io.FileOutputStream:

导入 java . io 异常:

导入 Java . security . invalidkeyexception;

导入 Java . security . nosuchalgorithm exception;

导入 javax . crypto . badpaddingexception:

导入 javax . crypto . cipher;

导入 javax.crypto。非法块异常:

导入 javax . crypto . key generator;

导入 javax . crypto . nosucpaddingexception:

导入 javax . crypto . spec . secretkeyspec;

import net . zenconsultant . Android . crypto . crypto;

导入 net . Zen consult . Android . model . contact;

导入 net . Zen consult . Android . model . location;

导入 Android . content . context;

导入 Android . OS . environment;

导入 android.util.Log:

公共类 SaveController {

private static final String TAG = " save controller ";

公共静态 void saveContact(上下文 Context,Contact contact) {

if (isReadWrite()) {

尝试{

文件输出文件 = 新文件(context.getExternalFilesDir image

(null),contact . get first name();

fileoutput stream output stream = new file output streamimage

(输出文件);

字节[] key =加密。generate keyimage

("randomtext".getBytes());

outputStream.write(encrypt(key,contact . getbytes()));

output stream . close();

} catch(file notfounindexception e)}

Log.e(标签,“找不到文件”);

} catch(io exception e)}

Log.e(标记,“io exception”);

}

} else {

Log.e(标签,“以读/写模式打开媒体卡时出错!”);

}

}

公共静态 void saveLocation(上下文上下文,位置位置){

if (isReadWrite()) {

尝试{

文件输出文件 = 新文件(context.getExternalFilesDir image

(null)、location . geti identifier();

fileoutput stream output stream = new file output streamimage

(输出文件);

字节[] key =加密。generate keyimage

("randomtext".getBytes());

outputStream.write(encrypt(key,location . getbytes()));

output stream . close();

} catch(file notfounindexception e)}

Log.e(标签,“找不到文件”);

} catch(io exception e)}

Log.e(标记,“io exception”);

}

} else {

Log.e(标签,“以读/写模式打开媒体卡时出错!”);

}

}

私有静态布尔值 isReadOnly() {

Log.e(标签,环境

。getexternalstragraestate());

回归环境。MEDIA_MOUNTED_READ_ONLY.equals(环境

。getexternalstragraestate());

}

私有静态布尔 isReadWrite() {

Log.e(标签,环境

。getexternalstragraestate());

回归环境。MEDIA_MOUNTED.equals(环境

。getexternalstragraestate());

}

私有静态字节[]加密(字节[]密钥,字节[]数据){

SecretKeySpec = new SecretKeySpec(key,“AES”);

密码密码;

字节[]塞浦路斯文本= null

尝试{

cipher = cipher . getinstance(" AES ");

cipher.init(密码。ENCRYPT_MODE,sKeySpec);

密文= cipher.doFinal(数据);

} catch(nosuchcalgorithexception e)}

Log.e(标记“nosuchcalgorithexception”);

} catch(nosuchtpaddingexception e)}

Log.e(标记“nosuchcpaddingexception”);

} catch(非法块异常 e)}

Log.e(标记," illegal block exception ");

} catch(badpaddingexception e)}

Log.e(标签," badpaddingexception ");

} catch(invalid key exception e)}

Log.e(标记," invalidkeyexception ");

}

返回密文;

}

}

锻炼

There are many ways to encrypt the data in our Proxim application. What I have done is to encrypt it at storage time. Your exercise is to rewrite the Proxim application so that the data is encrypted as soon as it is created.Tip Do not modify the SaveController.java file. Look elsewhere.Use the Android API reference and write a simple decryption routine based on the same principle as the encryption routine. Create a new class called LoadController that will handle the loading of information from the SD Card.

摘要

在移动设备上存储纯文本或其他容易阅读的数据是你应该不惜一切代价避免的事情。即使您的应用本身可能是安全编写的,来自设备上完全不同区域的间接攻击仍然可以收集和读取您的应用编写的敏感或个人信息。在应用设计期间,请遵循以下基本步骤:


  1. 首先,确定应用存储、创建或交换什么数据类型。接下来,将它们分类为个人数据或敏感数据,这样您将知道在应用执行期间如何处理这些数据。

  2. 拥有一个可以在应用中重用的加密例程集合。最好将此集合保存为一个单独的库,可以包含在项目中。

  3. 为您编写的每个应用生成一个不同的密钥。编写一个好的密钥生成器算法,创建冗长且不可预测的密钥。

  4. 在创建或存储时加密数据。


推荐阅读
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文介绍了在Linux下安装Perl的步骤,并提供了一个简单的Perl程序示例。同时,还展示了运行该程序的结果。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • Apache Shiro 身份验证绕过漏洞 (CVE202011989) 详细解析及防范措施
    本文详细解析了Apache Shiro 身份验证绕过漏洞 (CVE202011989) 的原理和影响,并提供了相应的防范措施。Apache Shiro 是一个强大且易用的Java安全框架,常用于执行身份验证、授权、密码和会话管理。在Apache Shiro 1.5.3之前的版本中,与Spring控制器一起使用时,存在特制请求可能导致身份验证绕过的漏洞。本文还介绍了该漏洞的具体细节,并给出了防范该漏洞的建议措施。 ... [详细]
  • 本文讨论了在ASP中创建RazorFunctions.cshtml文件时出现的问题,即ASP.global_asax不存在于命名空间ASP中。文章提供了解决该问题的代码示例,并详细解释了代码中涉及的关键概念,如HttpContext、Request和RouteData等。通过阅读本文,读者可以了解如何解决该问题并理解相关的ASP概念。 ... [详细]
  • 移动传感器扫描覆盖摘要:关于传感器网络中的地址覆盖问题,已经做过很多尝试。他们通常归为两类,全覆盖和栅栏覆盖,统称为静态覆盖 ... [详细]
  • 两个方案:1通过android.permission.READ_PHONE_STATE读取2通过短信过滤,读取手机号。按照第1个方案,AndroidManifest.xml需要添加< ... [详细]
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • MVC设计模式的介绍和演化过程
    本文介绍了MVC设计模式的基本概念和原理,以及在实际项目中的演化过程。通过分离视图、模型和控制器,实现了代码的解耦和重用,提高了项目的可维护性和可扩展性。详细讲解了分离视图、分离模型和分离控制器的具体步骤和规则,以及它们在项目中的应用。同时,还介绍了基础模型的封装和控制器的命名规则。该文章适合对MVC设计模式感兴趣的读者阅读和学习。 ... [详细]
  • Shodan简单用法Shodan简介Shodan是互联网上最可怕的搜索引擎,与谷歌不同的是,Shodan不是在网上搜索网址,而是直接进入互联网的背后通道。Shodan可以说是一款“ ... [详细]
  • 必须先赞下国人npm库作品:node-images(https:github.comzhangyuanweinode-images),封装了跨平台的C++逻辑,形成nodejsAP ... [详细]
  • 快速搭建SSM(Spring,SpringMVC,Mybatis)环境详细过程我是做移动(Android)开发的,这几天利用项目空隙大概学 ... [详细]
author-avatar
手机用户2602907485
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有