Encryption SAS中的数据掩蔽:在字符级对敏感观测值进行置乱

Encryption SAS中的数据掩蔽:在字符级对敏感观测值进行置乱,encryption,sas,masking,scramble,data-masking,Encryption,Sas,Masking,Scramble,Data Masking,我正在SAS中处理客户数据和敏感的客户身份信息。挑战在于如何屏蔽字段,使其保持数字/字母/字母数字。我找到了一种在SAS中使用按位函数的方法(BXOR、BOR、BAND),但输出中充满了特殊字符,SAS无法处理/排序/合并等 我还想根据一个键来对场本身进行置乱,但一直没能看穿。以下是挑战: 1) 它必须是基于密钥的 2) 必须是可逆的。 3) 屏蔽/加扰字段必须是数字/字母/字母数字,这样才能在SAS中使用。 4) 要屏蔽的字段既有字母表,也有数字,但长度各不相同,并且有数以百万计的观测值 任何

我正在SAS中处理客户数据和敏感的客户身份信息。挑战在于如何屏蔽字段,使其保持数字/字母/字母数字。我找到了一种在SAS中使用按位函数的方法(BXOR、BOR、BAND),但输出中充满了特殊字符,SAS无法处理/排序/合并等

我还想根据一个键来对场本身进行置乱,但一直没能看穿。以下是挑战:

1) 它必须是基于密钥的 2) 必须是可逆的。 3) 屏蔽/加扰字段必须是数字/字母/字母数字,这样才能在SAS中使用。 4) 要屏蔽的字段既有字母表,也有数字,但长度各不相同,并且有数以百万计的观测值


任何关于如何实现这种屏蔽/加扰的提示都会非常有用:(

SAS在其网站上有一篇关于如何加密特定变量的文章。希望这能对您有所帮助


这里是一个简单的基于密钥的解决方案。我在这里介绍了数据步解决方案,然后稍后将介绍FCMP版本。我将所有内容都保持在48到127之间(数字、字母和@><等常见字符);这不完全是字母数字,但我无法想象它在本例中的重要性。使用相同的方法,您可以将其进一步简化为真正的字母数字,但这会使键更糟糕(只有62个值),并且使用起来很笨拙(因为您有3个不连续的范围)

在这个解决方案中,有一个中等级别的加密-一个具有80个可能值的密钥不足以阻止一个真正复杂的黑客,但在大多数情况下都足够强大。您需要将密钥本身或种子传递给密钥算法才能解除加密;如果您多次使用此密钥,请确保选择一个新的种子每次(与数据无关)。如果使用零(或非postive整数)进行种子设定,则每次都可以有效地保证一个新密钥,但必须传递密钥本身而不是种子,这可能会带来一些数据安全问题(显然,密钥本身可能被恶意用户获取,并且必须存储在与数据不同的位置)。通过种子传递密钥可能更好,因为您可以通过电话或某种预先安排的种子列表口头传递

一般来说,我不确定我是否推荐这种方法;一种更好的方法可能是使用一种更好的加密方法(例如PGP)简单地加密整个SAS数据集。您的确切解决方案可能会有所不同,但如果您有一些客户信息,而这些信息在流程的大多数步骤中实际上并不需要,则最好将这些信息与其他(非敏感)数据分开,并仅在需要时合并这些信息

例如,我有一个流程,通过该流程,我为一个医疗保健调查的客户提取样本。我从一个数据集中选择有效记录,该数据集除了数字唯一标识符之外没有客户信息;一旦我将样本缩小到有效记录,然后我从一个单独的数据集附加客户信息并创建mailing文件(存储在加密目录中)。这使数据尽可能长时间不敏感。这并不完美-唯一的数字标识符仍然意味着有一个限制,即使它与项目外的任何人都不知道-但它尽可能长时间地保持安全

以下是FCMP版本:

%let keylength=5;
%let seed=15;

proc fcmp outlib=work.funcs.test;
subroutine encrypt(value $,key $);
  length key $&keylength.;
  outargs value,key;
  do _t = 1 to lengthc(value);
    substr(value,_t,1) = byte(mod(rank(substr(value,_t,1)) + rank(substr(key,1,1))-62,96)+31);
    key = substr(key,2)||substr(key,1,1);
  end;
endsub;

subroutine unencrypt(value $,key $);
  length key $&keylength.;
  outargs value,key;
  do _t = 1 to lengthc(value);
    substr(value,_t,1) = byte(mod(96+rank(substr(value,_t,1)) - rank(substr(key,1,1)),96)+31);
    key = substr(key,2)||substr(key,1,1);
  end;
endsub;

subroutine gen_key(seed,keystr $);
  outargs keystr;
  length keystr $&keylength.;
  do _t = 1 to &keylength.;
    _rannum = ceil(ranuni(seed)*80);    
    substr(keystr,_t,1)=byte(47+_rannum);
  end;
endsub;
quit;

options cmplib=work.funcs;



data encrypted;
set sashelp.class;
length key $&keylength.;
retain key ' '; *the missing is to avoid the uninitialized variable warning;
if _n_ = 1 then call gen_key(&seed,key);
call encrypt(name,key);
drop key;
run;

data unencrypted;
set encrypted;
length key $&keylength.;
retain key ' ';
if _n_ = 1 then call gen_key(&seed,key);
call unencrypt(name,key);
run;
这有点更健壮;它允许字符从32到127,而不是从48,这意味着它成功地处理了空格。(Tab仍然无法正确解码-它可能是一个“k”)。您将种子传递给调用gen_键,然后它将该键用于进程的其余部分


不用说,这并不能保证为您的目的发挥作用和/或成为一个安全的解决方案,如果您有重大的安全需求,您应该咨询安全专业人士。这篇文章不保证用于任何目的,并且海报不承担因其使用而产生的任何和所有责任。

使用l如何ookup表还是一种格式?什么是“仅数字/字母/字母数字”呢?它还能是什么?或者你是说它必须保持相同的类型[因此数字变量必须保持数字,字符变量必须保持字符]?还有,我们在寻找什么样的安全性?这是吗“阻止某人读取屏幕上的数据”安全,或“有人可能试图入侵的银行记录”"安全?Rob-客户端拒绝了单个查找,说如果有人想破解密码很容易。我最终开发了一个使用多个查找的代码,希望这就足够了。Joe-输出不应该有任何特殊字符。我尝试将输入转换为ASCII,然后转换为二进制,对其执行操作,然后转换为conv请将它们重新插入,但输出已被篡改,SAS无法读取。变量是客户的唯一标识符,因此客户端希望隐藏该值。这是我最后使用的:)链接现在已失效。请尝试在答案中提供最重要的信息,以便将来用户不会因为链接已损坏而感到沮丧。
%let keylength=5;
%let seed=15;

proc fcmp outlib=work.funcs.test;
subroutine encrypt(value $,key $);
  length key $&keylength.;
  outargs value,key;
  do _t = 1 to lengthc(value);
    substr(value,_t,1) = byte(mod(rank(substr(value,_t,1)) + rank(substr(key,1,1))-62,96)+31);
    key = substr(key,2)||substr(key,1,1);
  end;
endsub;

subroutine unencrypt(value $,key $);
  length key $&keylength.;
  outargs value,key;
  do _t = 1 to lengthc(value);
    substr(value,_t,1) = byte(mod(96+rank(substr(value,_t,1)) - rank(substr(key,1,1)),96)+31);
    key = substr(key,2)||substr(key,1,1);
  end;
endsub;

subroutine gen_key(seed,keystr $);
  outargs keystr;
  length keystr $&keylength.;
  do _t = 1 to &keylength.;
    _rannum = ceil(ranuni(seed)*80);    
    substr(keystr,_t,1)=byte(47+_rannum);
  end;
endsub;
quit;

options cmplib=work.funcs;



data encrypted;
set sashelp.class;
length key $&keylength.;
retain key ' '; *the missing is to avoid the uninitialized variable warning;
if _n_ = 1 then call gen_key(&seed,key);
call encrypt(name,key);
drop key;
run;

data unencrypted;
set encrypted;
length key $&keylength.;
retain key ' ';
if _n_ = 1 then call gen_key(&seed,key);
call unencrypt(name,key);
run;