从函数返回字符串(C语言)

6

我有一个返回字符串的函数:

const *char getMyPassword()
{
    return "mysecretpassword";
}

这个功能运行得很完美,但我发现在Unix系统上运行"strings"的话,它会出现在列表中,这不太好。

有什么最简单的方法可以替换它吗?该函数位于库中,我想保持其灵活性。现在我开始在函数内部进行malloc操作,这样字符串就不会出现在列表中了。但是,在什么情况下我应该再次释放它呢?

char * getMyPassword()
{
unsigned char arr[] = { 'p', 'a', 's', 's', 'w', 'o', 'r' , 'd', '\0' };
char *return_arr = malloc(sizeof(arr));
strcpy(return_arr, arr);
return return_arr;

}

如果在之前使用malloc并传递一个指针,我如何知道密码的大小,因为密码的大小只有在函数内部才知道?

作为备选方案,我可以传递一个大数组,但这似乎不太优雅。我该怎么做?

编辑:我添加了strcpy(return_arr,arr)。实际上,在原始代码中我已经加过了,但是在这里忘记了。


把它倒过来,然后使用strrev()。或者将其进行两次rot-13加密。 - Heath Hunnicutt
6
如果你要使用密码,我强烈建议使用哈希密码而不是明文字符串。这将大大减少他人通过对程序运行“strings”而获得的信息量。你可以自定义一个哈希函数,使人们无法(或者至少更困难地)确定该值的实际内容。 - dbeer
“strings”将读取以空字符结尾的内容,但是为此,您可能不想在应用程序中进行硬编码。为什么不使用配置文件,您可以单独保护它呢? - wkl
回答您之前的问题。一种做法是创建一个不可全局读取的配置文件(在-IX中采用类似600权限的方式,Windows也可以实现同样的效果),并赋予您的应用程序读取该文件的特权(通常在与拥有该配置文件的用户帐户相同的用户账户下运行)。 - Alexander Pogrebnyak
返回类型不应该是const *char而应该是 const char *,怎么可能会忽略这样一个明显的错误呢? - ajay
显示剩余4条评论
5个回答

2
除了安全问题,你正在尝试动态分配缓冲区。
你可以采取两种方法:
1. 在函数内部始终使用`malloc`并记录它返回的`malloced`结果。 2. 遵循某些标准库函数的做法,要求用户传递指向有效缓冲区及其大小的指针,并返回实际复制的大小。其中一些函数允许通过`check`,当你传入一个`null`缓冲区时,它们不会尝试分配它,而是返回所需的结构大小。
在我看来,你已经实现了第一种方法。
对于第二种方法,请使用以下签名:
int getMyPassword( char* buffer, size_t buff_size, size_t* p_num_copied )
{
  unsigned char arr[] = { 'p', 'a', 's', 's', 'w', 'o', 'r' , 'd', '\0' };

  if ( buffer == 0 )
  {
    *p_num_copied = sizeof(arr);
    return SUCCESS;
  }

  if ( buff_size < sizeof(arr) )
  {
    *p_num_copied = sizeof(arr);
    return BUFFER_TOO_SMALL;
  }

  memcpy( buffer, arr, sizeof(arr) );

  *p_num_copied = sizeof( arr );

  return SUCCESS;
}

方法#2的优点在于,调用者在很多情况下可以在堆栈上分配缓冲区,特别是如果你宣传最大所需缓冲区大小。另一个优点是内存管理现在完全由客户端处理。在通用库中,你不希望让客户端依赖于特定的库内存分配方案。
回复评论:
如果你总是想在客户端代码中使用已分配的值,那么我会这样做:
char* clientGetPasswordFromLibrary( )
{
  // in our day and age it's fine to allocate this much on stack
  char buffer[256];
  size_t num_copied;

  int status = getMyPassword( buffer, sizeof(buffer), &num_copied );

  char* ret_val = NULL;

  if ( status == BUFFER_TOO_SMALL )
  {
    ret_val = malloc( num_copied );

    if ( ret_val == NULL )
    {
      return NULL;
    }

    getMyPassword( ret_val, num_copied, &num_copied );
  }
  else
  {
    ret_val = malloc( num_copied );

    if ( ret_val == NULL )
    {
      return NULL;
    }

    memcpy( ret_val, buffer, num_copied );
  }

  return ret_val;
}

我曾经阅读过一篇文章,它说在分配内存并返回时是不好的做法,因此我一直试图避免这样做。所以谢谢你的第二种方法Alex。你觉得传递缓冲区然后重新分配它怎么样? - Frank Vilea
@Frank。如果在您的客户端代码中始终要依赖于malloc,那没问题。我将编辑答案以向您展示我如何做到这一点。 - Alexander Pogrebnyak

2
我认为问题在于,当你尝试返回一个指向本地定义变量的指针时,你正在尝试返回一个字符串。我期望你的第一个函数能够工作,因为指针返回一个文字的地址,这个文字在程序执行期间是静态的。同样,你可以在本地范围内声明char []变量为静态,这样它就不在本地范围内了。
但是说实话,我不明白为什么你想让一个函数返回一个指向字符串字面量的指针;当你可以简单地在本地范围之外定义该字面量,而它实际上是需要的。

原因是该函数位于我在运行时动态加载的库中。我这样做是因为我的实时系统使用与测试系统不同的密码。 - Frank Vilea

2
我有几个想法:
  1. 存储密码的哈希版本。在getMyPassword()中,对变量进行解码并返回它。

  2. 将密码存储在受保护的文件中(加密,仅为您的用户提供读取权限等)。从文件中加载密码并在您的函数中返回。

  3. 结合1和2-在受保护的文件中存储哈希密码,对其进行解码并返回。

这完全取决于您要求的安全性。

我非常喜欢这个想法,已经使用了你的rot13哈希程序。我将它放入加密库中,并在需要的二进制文件中进行解密。我将其保留为数组样式,以确保没有人可以简单地使用字符串。现在我想尝试一下,是否可以对库应用不同的权限,而不会破坏我的现有应用程序。 - Frank Vilea

1

这是一个或多或少徒劳的隐写术练习(你知道,任何人都可以通过反向工程您的程序轻易获取密码)。例如,为了更加增强安全性,您可以执行以下操作:

  1. 选择要与所有字符进行XOR运算的“种子”值。
  2. getMyPassword函数接受该字符。 这样,如果不知道种子,该函数基本上是无用的。

因此,例如,将值55作为种子。 您可以得到如下内容:

char * getMyPassword(char seed)
{
const char* str = "SEX@DDVG";
char *return_arr = malloc(9); /* 8+1 */
for (int i=0 ; i < 9 ; ++i)
   return_arr[i] = str[i] ^ seed;
return_arr[i] = 0;
return return_arr;
}

你需要调用getMyPassword(55)来获取正确的结果。我发誓,数字55是随机选择的,我不知道DDVG是什么 :)


非常简单而有效。谢谢你的建议,Diego。我可能会将它与我刚添加的一个非常简单的rot13哈希器结合起来。 - Frank Vilea
这太容易了。如果我查看库的公共符号并看到一个名为getMyPassword的函数,我会加倍努力去反汇编该函数。如果我后来获得了客户数据,如信用卡信息......必须有另一种方法将密码输入程序中。我会在程序启动后打开/dev/tty并从键盘读取它。将其放入lib或config文件中太不安全了。 - ott--

0
关于安全问题,将密码安全地传递给进程的常见方法是使用环境变量。例如,如果您的程序最终从网页调用,则可以在安全的Apache配置文件中设置密码(通过SetEnv PASSWORD "secret"),然后它将传递到运行的每个CGI脚本及其运行的任何内容中。然后,在您的程序中,您只需要嵌入getenv("PASSWORD")。
一个常见的错误是在程序的命令行上接受密码,这样做是不应该的,因为命令行可以被/proc中的任何进程访问。这就是'ps'可以显示正在运行的内容的原因。
此外,您可以将程序的权限设置为可执行但不可读(chmod -r+x program)。这样它就可以运行,但实际上无法读取其内容。对于Web服务树中的任何内容来说,这总是一个好主意,以避免通过服务器配置错误意外暴露东西。对于shell脚本是否适用取决于实现,但对于编译的程序它是有效的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接