SAS 中的 ODBC 密码安全

发布于 2024-12-16 19:05:55 字数 2669 浏览 0 评论 0原文

我们希望从 SAS 代码中的 ODBC 连接字符串中删除硬编码密码,并防止任何密码出现在 SAS 日志文件中。

似乎有很多白皮书讨论如何解决这个问题,但我要么发现它们存在问题,要么无法让它们工作。

每次都提示用户输入密码并不是一个可行的选择。 此外,将密码存储在宏变量中也是一种可接受的方法,只要您有办法在打开 MACROGEN 和 SYMBOLGEN 选项的情况下禁止将密码打印到日志中。

尝试 1 - 编码 (链接到此处的白皮书

proc pwencode in='mypassword' method=sasenc;
run;

给出:

{sasenc}ACFD24061BF77D7D5362EE7C2D00D08B

如果我用代码中的编码值替换明文密码,则 ODBC 直通语句运行正常。

proc sql noprint;
  connect to odbc as remote (datasrc=cmg_report user=myuser password='{sasenc}68B279564BD2695538CDCDB301E8A357563480B0');
  create table sqlo as 
  select *
  from connection to remote
  (
  select top 1 * from application
  )
  ;
  disconnect from remote;
quit;

并且日志正确地屏蔽了带有 XXXXXXX 的值。

961  proc sql noprint;
962    connect to odbc as remote (datasrc=cmg_report user=&user_cmg password=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX);
963    create table sqlo as
964    select *
965    from connection to remote
966    (
967    select top 1 * from application
968    )
969    ;
971  quit;
NOTE: Table WORK.SQLO created, with 1 rows and 29 columns.
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.34 seconds
      cpu time            0.01 seconds

上述方法的问题在于,如果有人有权访问该代码,他们可以使用加密密码登录,而无需知道明文密码。因此,虽然它隐藏了实际密码,但它并不能提供安全性。对我来说似乎有点傻或者我错过了什么? 编辑:如果您的 ODBC 密码碰巧在其他地方使用,这会提供一定的安全性,仅此而已。

尝试 2 - 使用 SYMGET此处链接到白皮书

问题是我根本无法让所描述的技术在 SAS 中发挥作用。我在 XP 上运行 SAS 9.2,尝试连接到 SQL Server 数据库。

%let my_password = password;

proc sql noprint;
  connect to odbc (dsn=cmg_report uid=myuser pwd=symget('my_password'));
  create table sqlo as 
  select *
  from connection to remote
  (
  select top 1 * from application
  )
  ;
quit;

我收到以下消息,表示登录失败:

1034      proc sql noprint;
1035        connect to odbc (dsn=cmg_report uid=myuser pwd=XXXXXX('my_password'));

ERROR: CLI error trying to establish connection: [Microsoft][SQL Server Native Client 10.0][SQL
Server]Login failed for user 'myuser'.

看起来它正在尝试使用“symget”作为实际密码(因为它已在日志中被屏蔽)。对此白皮书有一些回应,称将 symget 包装在 %sysfunc 调用中,但 symget() 函数是 SAS 不允许在 %sysfunc 调用中使用的少数函数之一,因此我不明白这怎么可能。

任何其他提示/建议/想法将不胜感激。

谢谢

编辑:如果有一种技术可以在打开options symbolgen Macrogen的情况下执行此操作,那就特别好。

We want to remove hardcoded passwords from ODBC connection strings in our SAS code, and also prevent any of the passwords from appearing in the SAS log files.

There seems to be plenty of whitepapers discussing how to go about this but I either find problems with them, or can't get them working.

Prompting the user each time for the PW is not a viable alternative.
Also, storing the password in a macro variable is an acceptable approach, as long as you have a way to suppress it from printing to the log with MACROGEN and SYMBOLGEN options turned on.

ATTEMPT 1 - ENCODING (link to whitepaper here)

proc pwencode in='mypassword' method=sasenc;
run;

gives:

{sasenc}ACFD24061BF77D7D5362EE7C2D00D08B

If I replace my plaintext password with the encoded value in my code then the ODBC passthrough statement runs fine.

proc sql noprint;
  connect to odbc as remote (datasrc=cmg_report user=myuser password='{sasenc}68B279564BD2695538CDCDB301E8A357563480B0');
  create table sqlo as 
  select *
  from connection to remote
  (
  select top 1 * from application
  )
  ;
  disconnect from remote;
quit;

And the log correctly masks out the values with XXXXXXXs.

961  proc sql noprint;
962    connect to odbc as remote (datasrc=cmg_report user=&user_cmg password=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX);
963    create table sqlo as
964    select *
965    from connection to remote
966    (
967    select top 1 * from application
968    )
969    ;
971  quit;
NOTE: Table WORK.SQLO created, with 1 rows and 29 columns.
NOTE: PROCEDURE SQL used (Total process time):
      real time           0.34 seconds
      cpu time            0.01 seconds

The problem with the above approach is that if someone has access to the code, they can login using the encrypted password, without needing to know the plain text password. So while it hides the actual password it doesn't provide security. Seems kind of silly to me or am I missing something? EDIT: This provides some security if your ODBC password happens to be used elsewhere, that's about it though.

ATTEMPT 2 - USING SYMGET (link to whitepaper here)

The problem with this is that I simply can't get the technique described to work in SAS. I'm running SAS 9.2 on XP, trying to connect to an SQL Server DB.

%let my_password = password;

proc sql noprint;
  connect to odbc (dsn=cmg_report uid=myuser pwd=symget('my_password'));
  create table sqlo as 
  select *
  from connection to remote
  (
  select top 1 * from application
  )
  ;
quit;

I get the below message saying that the login failed:

1034      proc sql noprint;
1035        connect to odbc (dsn=cmg_report uid=myuser pwd=XXXXXX('my_password'));

ERROR: CLI error trying to establish connection: [Microsoft][SQL Server Native Client 10.0][SQL
Server]Login failed for user 'myuser'.

It looks like it is trying to use "symget" as the actual password (as it has been masked out in the log). There are some responses to this whitepaper saying to wrap the symget in a %sysfunc call but the symget() function is one of the few functions that SAS does not allow within a %sysfunc call so I don't see how that could be possible.

Any other tips/suggestions/ideas would be much appreciated.

Thanks

EDIT: It would be especially good if there was a technique to do this that worked with options symbolgen macrogen turned on.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

童话 2024-12-23 19:05:55

Rob,我们遇到了类似的问题,并提出了一种不同的方法,允许我们所有的团队成员运行相同的程序,而无需将我们的 ID/密码存储在程序中。它要求每个团队成员都有一个 SAS 可以访问的安全存储的文本文件(除所有者之外没有任何权限)。

以下是 ID/PW 文件内容的示例:

machine odbc login XX_odbc_id_XX password XXodbc_pw_XX
machine oracle login XX_oracle_id_XX password XX_oracle_pw_XX

我们在 UNIX 服务器上运行,因此我们将各个 id/pw 文件锁定在我们的主目录中,这样就没有其他人可以访问它,在本例中它被命名为“ .netrc”。该线程末尾的宏应该存储在某个地方,那么程序将如下所示:

%let id_pw_text_file = ~/.netrc;

%ODBC_Acct;

proc sql;
   %ODBC_Connect
   create table sqlo as
      select * from connection to odbc
      (
          /*  [ Insert ODBC query here ]  */ 
      );
   %ODBC_Disconnect
   quit;
run;

我尝试修改宏以在您的环境中工作并删除大量特定于我们系统的代码,但显然我没有这样做无法测试它以确保它有效。如果您遇到问题,请告诉我,我会尽力帮助解决。希望这有帮助。

/*********************************************************************
*  Name:  ODBC_Acct                                                  *
*  Desc:  Set global macro vars containing a users ODBC username     *
*         and password. Retrieves this information from a users      *
*         specific ID/PW file.                                       *
*********************************************************************/
%macro ODBC_Acct( mprint );
   %local __mprint __symbolgen __mlogic;
   %if ( %length( &mprint ) = 0 ) %then %let mprint = NO;
   %if ( %upcase( &mprint ) = NO ) %then %do;
      %let __mprint = %sysfunc( getoption( mprint ));
      %let __symbolgen = %sysfunc( getoption( symbolgen ));
      %let __mlogic = %sysfunc( getoption( mlogic ));
      options nomprint nosymbolgen nomlogic;
   %end;
   %global  odbc_user  odbc_pw;
   %Get_ID_PW( &id_pw_text_file , odbc , odbc_user , odbc_pw )
   %if ( %upcase(&__mprint) ne NOMPRINT ) %then %do;
      options &__mprint &__symbolgen &__mlogic;
   %end;
%mend;

/*********************************************************************
*  Name:  ODBC_Connect, ODBC_Disconnect                              *
*  Desc:  Returns SAS/Access connect or disconnect statements        *
*         for accessing ODBC.                                        *
*********************************************************************/
%macro ODBC_Connect( mprint=no );
   %local __mprint __symbolgen;
   %if ( %upcase(&mprint) = NO ) %then %do;
      %let __mprint = %sysfunc( getoption( mprint ));
      %let __symbolgen = %sysfunc( getoption( symbolgen ));
      options nomprint nosymbolgen;
   %end;
   connect to odbc as remote (
      datasrc=cmg_report
          user = "&odbc_user"
      password = "&odbc_pw"
      );
   %if ( %upcase(&__mprint) ne NOMPRINT ) %then %do;
      options &__mprint &__symbolgen;
   %end;
%mend;
%macro ODBC_Disconnect;
   disconnect from odbc;
%mend;

/*******************************************************************************
*  Name:  GetID_PW                                                             *
*  Desc:  Get loginid and password from a secured file                         *
*------------------------------------------------------------------------------*
*  Arguments:                                                                  *
*    1st   Required. Source file containing IDs and passwords.                 *
*    2nd   Required. Host id.                                                  *
*    3rd   Required. Specify the macro variable to put the loginid.            *
*    4th   Required. Specify the macro variable to put the password.           *
*------------------------------------------------------------------------------*
*******************************************************************************/
%macro Get_ID_PW( source , rhost , usrvar , pw_var );
   %let source_file = &source
   %if ( %sysfunc( fileexist( &source_file ) ) ) %then %do;
      %let rc  = %sysfunc( filename( dummy , &source_file ) );
      %let fid = %sysfunc( fopen( &dummy ) );
      %do %while( %sysfunc( fread( &fid ) ) = 0 );
         %let rc = %sysfunc( fget( &fid , inrec , 500 ) );
         %let machine = %scan( &inrec , 2 , %str( ) );
         %if ( %upcase( &machine ) = %upcase( &rhost ) ) %then %do;
            %let &usrvar = %scan( &inrec , 4 , %str( ) );
            %let &pw_var = %scan( &inrec , 6 , %str( ) );
            %goto Break;
         %end;
      %end;
      %Break: %*;
      %let rc = %sysfunc( fclose( &fid ) );
      %let rc = %sysfunc( filename( dummy ) );
   %end;
   %else %do;
       %put ::: ID/PW file "&source_file" not found;
   %end;
%mend;

Rob, we ran into a similar issue and came up with a different method that allows all of our team members to run the same program without having our id/passwords stored in the programs. It requires that each team member have a text file stored safely (no permissions except for owner) that SAS can access.

Here is an example of contents of an ID/PW file:

machine odbc login XX_odbc_id_XX password XXodbc_pw_XX
machine oracle login XX_oracle_id_XX password XX_oracle_pw_XX

We operate on a UNIX server, so we store our indivual id/pw files locked up in our home directory so no one else can access it, in this case it is named ".netrc". The macros at the end of this thread should be stored somewhere, then the program would look like the following:

%let id_pw_text_file = ~/.netrc;

%ODBC_Acct;

proc sql;
   %ODBC_Connect
   create table sqlo as
      select * from connection to odbc
      (
          /*  [ Insert ODBC query here ]  */ 
      );
   %ODBC_Disconnect
   quit;
run;

I tried to revise the macros to work in your environment and to remove a lot of code specific to our systems, but obviously I wasn't able to test it to make sure it works. Let me know if you have an issue and I'll try to help fix it. Hope this helps.

/*********************************************************************
*  Name:  ODBC_Acct                                                  *
*  Desc:  Set global macro vars containing a users ODBC username     *
*         and password. Retrieves this information from a users      *
*         specific ID/PW file.                                       *
*********************************************************************/
%macro ODBC_Acct( mprint );
   %local __mprint __symbolgen __mlogic;
   %if ( %length( &mprint ) = 0 ) %then %let mprint = NO;
   %if ( %upcase( &mprint ) = NO ) %then %do;
      %let __mprint = %sysfunc( getoption( mprint ));
      %let __symbolgen = %sysfunc( getoption( symbolgen ));
      %let __mlogic = %sysfunc( getoption( mlogic ));
      options nomprint nosymbolgen nomlogic;
   %end;
   %global  odbc_user  odbc_pw;
   %Get_ID_PW( &id_pw_text_file , odbc , odbc_user , odbc_pw )
   %if ( %upcase(&__mprint) ne NOMPRINT ) %then %do;
      options &__mprint &__symbolgen &__mlogic;
   %end;
%mend;

/*********************************************************************
*  Name:  ODBC_Connect, ODBC_Disconnect                              *
*  Desc:  Returns SAS/Access connect or disconnect statements        *
*         for accessing ODBC.                                        *
*********************************************************************/
%macro ODBC_Connect( mprint=no );
   %local __mprint __symbolgen;
   %if ( %upcase(&mprint) = NO ) %then %do;
      %let __mprint = %sysfunc( getoption( mprint ));
      %let __symbolgen = %sysfunc( getoption( symbolgen ));
      options nomprint nosymbolgen;
   %end;
   connect to odbc as remote (
      datasrc=cmg_report
          user = "&odbc_user"
      password = "&odbc_pw"
      );
   %if ( %upcase(&__mprint) ne NOMPRINT ) %then %do;
      options &__mprint &__symbolgen;
   %end;
%mend;
%macro ODBC_Disconnect;
   disconnect from odbc;
%mend;

/*******************************************************************************
*  Name:  GetID_PW                                                             *
*  Desc:  Get loginid and password from a secured file                         *
*------------------------------------------------------------------------------*
*  Arguments:                                                                  *
*    1st   Required. Source file containing IDs and passwords.                 *
*    2nd   Required. Host id.                                                  *
*    3rd   Required. Specify the macro variable to put the loginid.            *
*    4th   Required. Specify the macro variable to put the password.           *
*------------------------------------------------------------------------------*
*******************************************************************************/
%macro Get_ID_PW( source , rhost , usrvar , pw_var );
   %let source_file = &source
   %if ( %sysfunc( fileexist( &source_file ) ) ) %then %do;
      %let rc  = %sysfunc( filename( dummy , &source_file ) );
      %let fid = %sysfunc( fopen( &dummy ) );
      %do %while( %sysfunc( fread( &fid ) ) = 0 );
         %let rc = %sysfunc( fget( &fid , inrec , 500 ) );
         %let machine = %scan( &inrec , 2 , %str( ) );
         %if ( %upcase( &machine ) = %upcase( &rhost ) ) %then %do;
            %let &usrvar = %scan( &inrec , 4 , %str( ) );
            %let &pw_var = %scan( &inrec , 6 , %str( ) );
            %goto Break;
         %end;
      %end;
      %Break: %*;
      %let rc = %sysfunc( fclose( &fid ) );
      %let rc = %sysfunc( filename( dummy ) );
   %end;
   %else %do;
       %put ::: ID/PW file "&source_file" not found;
   %end;
%mend;
dawn曙光 2024-12-23 19:05:55

因此,我还联系了 SAS,了解他们对此类问题的建议,这是他们(一如既往的及时)回复。不幸的是,如果不禁用符号生成,他们就无法实现这一目标:

为了防止密码被硬编码到 SAS 程序中,或者
针对出现在 SAS 日志中的情况,建议采用以下方法:

1) 最安全的选项是使用以下命令发出 LIBNAME 语句
所需的 SAS/Access 引擎,并指定 DBPROMPT=YES。这会提示
您可以在 SAS 代码运行时获取数据库的连接信息,
这样您的程序中就不会存储任何连接信息。

但是,由于这在运行时需要一些手动交互
工作,这对于您的情况可能不可行。

2) 数据库连接信息可以存储在SAS注册表中
并且连接所需的密码将被加密。为了
要进行设置,请以交互方式运行 SAS,然后从资源管理器窗口中,
突出显示“库”。从 p 菜单中,选择“文件”-“新建”。输入
libref 您希望在“名称”框中用于数据库连接,
然后从下拉菜单中选择正在使用的数据库引擎。
选择引擎后,您将看到一个窗口,允许
您输入用户名、密码、路径和选项。填写您的
连接信息,然后点击右上角的小按钮
表示“启动时启用”。此方法存储您的连接
SAS 注册表中的信息,并会在以下情况下自动连接
你启动 SAS。当以批处理模式运行 SAS 时,您必须指定
-startlib 在调用时以便分配库。您的密码将作为加密值显示在 SAS 日志中。

3) 如果运行 PROC SQL Pass-Through,您可以将密码传递给 SAS
通过 SAS 调用时的 -sysparm 选项,并使用 &sysparm SAS
通常对密码进行编码的宏变量。一个例子
如下:

<块引用>

sas -nodms -sysparm mypassword

1?过程 SQL; 2?连接到 ORACLE(用户 = scott 密码 =“&sysparm”);

请注意,如果宏选项 MPRINT 和/或 SYMBOLGEN 有效,
解析的宏变量将出现在 SAS 日志中,因此,
您的密码将以纯文本形式显示在 SAS 日志中。 NOMPRINT 和
NOSYMBOLGEN 是默认设置。

4) 此外,如果运行 PROC SQL Pass-Through,您可以存储您的 CONNECT
受操作系统权限保护的文件中的语句,例如
只有您有读取权限,然后使用 %INCLUDE 语句
包括 CONNECT 语句。示例如下:

<块引用>

sas-nodms

1?选项无源2; 2?过程 SQL; 3? %INCLUDE 'myconnect.dat';

在上面的示例中,OPTIONS NOSOURCE2 阻止包含的代码
防止显示在 SAS 日志中。指定 SOURCE2 列出
SAS 日志中包含的文件的内容。 NOSOURCE2 是默认值
设置。

5) 对于 SAS 9.1 及更高版本,您可以使用 Proc PWENCODE 过程
将创建可用于代替纯文本的编码密码
SAS程序中批量密码。

http://support.sas.com/onlinedoc/ 913/getDoc/proc.hlp/a002595988.htm

然后选择 PWENCODE 过程。

Proc 语法如下所示。编码后的密码输出到日志。

proc pwencode in="plaintextPassword";运行;

So I also contacted SAS to see what they recommend for this type of issue and this was their (timely as always) response. Unfortunately it looks like their is no way to achieve this without disabling symbolgen:

In order to keep passwords from being hard-coded into SAS programs, or
from appearing in SAS logs, the following methods are suggested:

1) The most secure option is to issue a LIBNAME statement using the
required SAS/Access engine, and specify DBPROMPT=YES. This will prompt
you for connection information to the database as the SAS code runs,
so that no connection information will be stored in your program.

However, since this requires some manual interaction when running
jobs, it may not be feasible in your situation.

2) Database connection information can be stored in the SAS registry
and the password required for connection will be encrypted. In order
to set this up, run SAS interactively, and from the Explorer window,
highlight "Libraries". From the pmenus, select File-New. Enter the
libref you wish to use for your database connection in the "Name" box,
then select the database engine being used from the pull-down menu.
Once the engine has been selected, you will see a window that allows
you to enter username, password, path, and options. Fill in your
connection information, then click the small button on the upper right
that indicates "Enable at Startup". This method stores your connection
information in the SAS registry and will automatically connect when
you launch SAS. When running SAS in batch mode, you MUST specify
-startlib at invocation in order for the library to be allocated. Your password will appear in your SAS log as an encrypted value.

3) If running PROC SQL Pass-Through, you can pass the password to SAS
via the -sysparm option at SAS invocation, and use the &sysparm SAS
macro variable where the password would normally be coded. An example
follows:

sas -nodms -sysparm mypassword

1? PROC SQL; 2? CONNECT TO ORACLE(user=scott password="&sysparm");

Note that if the macro options MPRINT and/or SYMBOLGEN are in effect,
the resolved macro variable will appear in the SAS log, and therefore,
your password will appear in plain text in your SAS log. NOMPRINT and
NOSYMBOLGEN are the default settings.

4) Also, if running PROC SQL Pass-Through, you can store your CONNECT
statement in a file protected by operating system permissions such
that only you have read permission, then use the %INCLUDE statement to
include the CONNECT statement. An example follows:

sas -nodms

1? OPTIONS NOSOURCE2; 2? PROC SQL; 3? %INCLUDE 'myconnect.dat';

In the above example, OPTIONS NOSOURCE2 prevents the included code
from being displayed in the SAS log. Specifying SOURCE2 lists the
contents of the included file in the SAS log. NOSOURCE2 is the default
setting.

5) With SAS 9.1 and later, you can use Proc PWENCODE procedure that
will create encoded password can be used in place of plain-text
passwords in SAS programs in batch.

http://support.sas.com/onlinedoc/913/getDoc/proc.hlp/a002595988.htm

and select PWENCODE Procedure.

Proc syntax is listed below. The encoded password is output to log.

proc pwencode in="plaintextPassword"; run;

笨笨の傻瓜 2024-12-23 19:05:55

当 SYMGET 在 CONNECT 语句中不起作用时,请尝试使用 %SUPERQ 引用功能。它还解析宏变量,而不将其显示在日志中。

when SYMGET does not work in the CONNECT statement, try the %SUPERQ quoting function. It also resolves the macro variable without surfacing it in the LOG.

三生殊途 2024-12-23 19:05:55

抱歉,无法写评论,但是 %superq-Trick 确实有效。像这样使用它:

proc sql;
   select * from set_encrypedon (pw="%SUPERQ(_password)");
quit;

其中有一个名为 _password 的宏变量。

不幸的是,这不能封装到宏中,

%macro pwd();
  "%SUPERQ(_password)"
%mend;

因为这样 MPRINT 将再次记录您的密码。

Sorry, can't write a comment, but the %superq-Trick really works. Use it like this:

proc sql;
   select * from set_encrypedon (pw="%SUPERQ(_password)");
quit;

where you have a macro variable called _password.

Unfortunately, this can't be wrapped into a macro like

%macro pwd();
  "%SUPERQ(_password)"
%mend;

because then MPRINT will log your password again.

春夜浅 2024-12-23 19:05:55

没有安全的方法来定义 ODBC 连接!

每个人都可以使用元数据 API 读取身份验证域、用户名并对密码进行编码。 Linux 管理员或root 用户可以访问任何文件系统,包括主目录及其password.sas 文件。

SAS 还提供 ChangePassPhrase 将存储的密码降级为较旧的 SAS PWENCODE 方法。如果您知道编码后的密码,则可以在线对其进行解码,例如 https://decrypt- password.appspot.com/sas-pwdecode/

有时登录凭据用于不同的服务。 FTP 或 MAIL 的用户名和密码也可用于 ssh。文件和元数据不是安全存储。

There is no secure method to define a ODBC connection!

Everybody can read auth domains, usernames and encode passwords by using the Metadata API. The linux admin or root user can access to any file system, including the home directories and it's password.sas files.

SAS also provide ChangePassPhrase to downgrade stored passwords to older SAS PWENCODE method. If you know the encoded password, than you can decode it for example online at https://decrypt-password.appspot.com/sas-pwdecode/

Sometime login credentials used for different services. A username and password for FTP or MAIL could also use for ssh. Files and metadata are not a secure store.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文